Secrets Management¶
Overview¶
GitOps requires secrets to be declarative without storing plaintext credentials in Git. Choose one approach and use it consistently.
Recommended Options¶
| Option | Best fit | Trade-off |
|---|---|---|
| SOPS + age | Small self-hosted cluster | Simple and Git-native; protect the age private key separately |
| External Secrets Operator | Existing Vault/cloud secret manager | Central rotation; depends on an external backend |
| Sealed Secrets | Kubernetes-only workflow | Easy to adopt; ciphertext is tied to the controller key |
For one VPS, SOPS + age has the smallest operational footprint. However, Argo CD needs a securely managed decryption plugin. Use External Secrets instead when a secret backend already exists.
External Secrets Pattern¶
Install External Secrets Operator as a platform Application, configure one SecretStore or ClusterSecretStore, then commit only references:
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: example-api
spec:
refreshInterval: 1h
secretStoreRef:
name: production
kind: ClusterSecretStore
target:
name: example-api
data:
- secretKey: DATABASE_URL
remoteRef:
key: example-api/database-url
Verification¶
kubectl get externalsecret,secretstore,clustersecretstore -A
kubectl describe externalsecret example-api
Use separate application credentials, rotate bootstrap passwords, and prevent secrets from appearing in Helm values, shell history, Argo CD parameters, or logs.