Skip to main content

Secrets Management

Configure secrets for your self-hosted Align deployment.

Required Secrets

Align requires these secrets to function:

SecretPurpose
Database credentialsPostgreSQL connection
Internal secretsJWT, cookies, encryption
LLM API keysOpenAI/Anthropic (or custom)
OAuth credentialsSlack, Teams, Jira, GitHub

Secret Management Options

Choose the approach that fits your infrastructure:

MethodBest For
SOPSGitOps workflows, encrypted at rest
Sealed SecretsKubernetes-native, cluster-specific encryption
External SecretsAWS/GCP/Azure secret managers
VaultHashiCorp Vault users
ManualSimple deployments, testing

SOPS encrypts secrets in Git while keeping them decryptable in your cluster.

Setup

  1. Install SOPS and configure your KMS key:
# Install SOPS
brew install sops # macOS
# or
apt-get install sops # Linux

# Configure KMS (example: AWS KMS)
export SOPS_KMS_ARN="arn:aws:kms:region:account:key/key-id"
  1. Create a .sops.yaml configuration:
# .sops.yaml
creation_rules:
- path_regex: secrets/.*\.yaml$
kms: arn:aws:kms:eu-west-2:123456789:key/your-key-id
  1. Create and encrypt secrets:
# Create plaintext secret file
cat > secrets/align-secrets.yaml << 'EOF'
apiVersion: v1
kind: Secret
metadata:
name: align-internal
namespace: align
type: Opaque
stringData:
jwt-secret: "your-jwt-secret-here"
cookie-secret: "your-cookie-secret-here"
encryption-key: "your-encryption-key-here"
service-auth-token: "your-service-auth-token"
EOF

# Encrypt with SOPS
sops -e -i secrets/align-secrets.yaml

# File is now encrypted, safe to commit
git add secrets/align-secrets.yaml
  1. Decrypt and apply (in CI/CD or manually):
sops -d secrets/align-secrets.yaml | kubectl apply -f -

With ArgoCD

Use the KSOPS plugin:

# kustomization.yaml
generators:
- ./secret-generator.yaml

# secret-generator.yaml
apiVersion: viaduct.ai/v1
kind: ksops
metadata:
name: secret-generator
files:
- ./secrets/align-secrets.enc.yaml

With Flux

Use the SOPS integration:

# kustomization.yaml (Flux)
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: align
spec:
decryption:
provider: sops
secretRef:
name: sops-gpg

Option 2: Sealed Secrets

Bitnami Sealed Secrets encrypts secrets with a cluster-specific key.

Setup

  1. Install Sealed Secrets controller:
helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
helm install sealed-secrets sealed-secrets/sealed-secrets \
--namespace kube-system
  1. Install kubeseal CLI:
brew install kubeseal  # macOS
  1. Create and seal secrets:
# Create plaintext secret (don't commit this!)
cat > /tmp/secret.yaml << 'EOF'
apiVersion: v1
kind: Secret
metadata:
name: align-internal
namespace: align
stringData:
jwt-secret: "your-jwt-secret"
cookie-secret: "your-cookie-secret"
EOF

# Seal it
kubeseal -o yaml < /tmp/secret.yaml > sealedsecrets/align-internal.yaml

# Delete plaintext
rm /tmp/secret.yaml

# Safe to commit sealed version
git add sealedsecrets/align-internal.yaml
  1. Apply sealed secret:
kubectl apply -f sealedsecrets/align-internal.yaml
warning

Sealed Secrets are cluster-specific. If you recreate your cluster, you'll need to re-seal all secrets with the new controller key.


Option 3: External Secrets Operator

Pull secrets from AWS Secrets Manager, GCP Secret Manager, or Azure Key Vault.

Setup (AWS)

  1. Install External Secrets Operator:
helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets external-secrets/external-secrets \
--namespace external-secrets \
--create-namespace
  1. Create a ClusterSecretStore:
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: aws-secrets-manager
spec:
provider:
aws:
service: SecretsManager
region: eu-west-2
auth:
jwt:
serviceAccountRef:
name: external-secrets
namespace: external-secrets
  1. Create ExternalSecret for Align:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: align-internal
namespace: align
spec:
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: aws-secrets-manager
target:
name: align-internal
data:
- secretKey: jwt-secret
remoteRef:
key: align/production
property: jwt-secret
- secretKey: cookie-secret
remoteRef:
key: align/production
property: cookie-secret
  1. Enable in Helm values:
externalSecrets:
enabled: true
secretStore:
name: aws-secrets-manager
kind: ClusterSecretStore
aws:
secretPath: "align/production"

Option 4: HashiCorp Vault

For organizations using Vault.

Setup

  1. Install Vault Agent Injector or External Secrets Vault provider

  2. Create Vault secrets:

vault kv put secret/align/production \
jwt-secret="your-jwt-secret" \
cookie-secret="your-cookie-secret" \
encryption-key="your-encryption-key"
  1. Configure External Secrets for Vault:
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: vault
spec:
provider:
vault:
server: "https://vault.example.com"
path: "secret"
auth:
kubernetes:
mountPath: "kubernetes"
role: "align"

Option 5: Manual Secrets

For simple deployments or testing.

# Generate random secrets
JWT_SECRET=$(openssl rand -base64 32)
COOKIE_SECRET=$(openssl rand -base64 32)
ENCRYPTION_KEY=$(openssl rand -base64 32)
SERVICE_TOKEN=$(openssl rand -base64 32)

# Create Kubernetes secret
kubectl create secret generic align-internal \
--namespace align \
--from-literal=jwt-secret="$JWT_SECRET" \
--from-literal=cookie-secret="$COOKIE_SECRET" \
--from-literal=encryption-key="$ENCRYPTION_KEY" \
--from-literal=service-auth-token="$SERVICE_TOKEN"
warning

Manual secrets are not GitOps-friendly and can be lost if the cluster is recreated. Use for development only.


Secret Reference

align-database

stringData:
url: "postgresql://user:pass@host:5432/align"
host: "your-db-host.example.com"
port: "5432"
username: "align"
password: "your-db-password"

align-internal

stringData:
jwt-secret: "32+ character random string"
cookie-secret: "32+ character random string"
encryption-key: "32+ character random string"
service-auth-token: "32+ character random string"

align-llm

stringData:
openai-api-key: "sk-..."
# OR
anthropic-api-key: "sk-ant-..."
# OR for custom providers
custom-base-url: "http://ollama:11434/v1"
custom-model: "llama3:70b"

align-oauth-slack

stringData:
client-id: "your-slack-client-id"
client-secret: "your-slack-client-secret"
signing-secret: "your-slack-signing-secret"

align-oauth-github

stringData:
client-id: "your-github-client-id"
client-secret: "your-github-client-secret"
app-id: "your-github-app-id"
app-private-key: |
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----

Best Practices

  1. Never commit plaintext secrets — Always encrypt or use external secret managers
  2. Rotate secrets regularly — Especially after team member departures
  3. Use separate secrets per environment — Don't share secrets between prod and staging
  4. Audit access — Use cloud provider audit logs for secret access
  5. Backup encryption keys — Store SOPS/KMS keys securely outside the cluster