Secrets Management
Configure secrets for your self-hosted Align deployment.
Required Secrets
Align requires these secrets to function:
| Secret | Purpose |
|---|---|
| Database credentials | PostgreSQL connection |
| Internal secrets | JWT, cookies, encryption |
| LLM API keys | OpenAI/Anthropic (or custom) |
| OAuth credentials | Slack, Teams, Jira, GitHub |
Secret Management Options
Choose the approach that fits your infrastructure:
| Method | Best For |
|---|---|
| SOPS | GitOps workflows, encrypted at rest |
| Sealed Secrets | Kubernetes-native, cluster-specific encryption |
| External Secrets | AWS/GCP/Azure secret managers |
| Vault | HashiCorp Vault users |
| Manual | Simple deployments, testing |
Option 1: SOPS (Recommended for GitOps)
SOPS encrypts secrets in Git while keeping them decryptable in your cluster.
Setup
- 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"
- Create a
.sops.yamlconfiguration:
# .sops.yaml
creation_rules:
- path_regex: secrets/.*\.yaml$
kms: arn:aws:kms:eu-west-2:123456789:key/your-key-id
- 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
- 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
- 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
- Install kubeseal CLI:
brew install kubeseal # macOS
- 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
- Apply sealed secret:
kubectl apply -f sealedsecrets/align-internal.yaml
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)
- 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
- 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
- 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
- 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
-
Install Vault Agent Injector or External Secrets Vault provider
-
Create Vault secrets:
vault kv put secret/align/production \
jwt-secret="your-jwt-secret" \
cookie-secret="your-cookie-secret" \
encryption-key="your-encryption-key"
- 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"
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
- Never commit plaintext secrets — Always encrypt or use external secret managers
- Rotate secrets regularly — Especially after team member departures
- Use separate secrets per environment — Don't share secrets between prod and staging
- Audit access — Use cloud provider audit logs for secret access
- Backup encryption keys — Store SOPS/KMS keys securely outside the cluster