GitOps says "everything in Git" — but a Kubernetes Secret is only base64, not encrypted. Committing one leaks it to everyone who can read the repo, forever. Choose one of these patterns instead:
| Pattern | What is in Git | Who decrypts |
|---|---|---|
| Sealed Secrets | A SealedSecret, encrypted with the controller's public key |
The sealed-secrets controller in the cluster |
| External Secrets Operator | An ExternalSecret that points to a key in a vault |
ESO fetches from AWS Secrets Manager, Azure Key Vault, GCP Secret Manager, HashiCorp Vault |
| SOPS (+ KSOPS / helm-secrets) | A YAML file encrypted with age/KMS | A plugin in the repo server at render time |
# Sealed Secrets: encrypt locally, commit only the sealed file
kubectl create secret generic shop-db --from-literal=password='changeme-example' \
--dry-run=client -o yaml | kubeseal --format yaml > sealed-shop-db.yaml
# External Secrets: Git holds a reference, the value stays in the vault
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata: { name: shop-db, namespace: shop-prod }
spec:
refreshInterval: 1h
secretStoreRef: { name: aws-secrets, kind: ClusterSecretStore }
target: { name: shop-db }
data:
- secretKey: password
remoteRef: { key: prod/shop/db, property: password }
External Secrets is the most common choice when the company already has a vault: rotation happens in the vault, Git never sees a value. Give the ExternalSecret an earlier sync wave than the Deployment that uses it.
Repository credentials
Argo CD needs to read private repos. Use a read-only deploy key (SSH) or a token with read scope only — never a person's account.
ssh-keygen -t ed25519 -f argocd-shop-config -N '' -C argocd@example.com # add the .pub as a read-only deploy key
argocd repo add git@github.com:example-org/shop-config.git --ssh-private-key-path argocd-shop-config
argocd repo list
Declaratively, a repository is a Secret with a special label:
apiVersion: v1
kind: Secret
metadata:
name: repo-shop-config
namespace: argocd
labels:
argocd.argoproj.io/secret-type: repository # or repo-creds for a URL-prefix template
stringData:
url: git@github.com:example-org/shop-config.git
sshPrivateKey: |
(from your secret store - do not commit this file)
repo-credsSecrets are credential templates: one forhttps://github.com/example-org/covers every repo under it.- GitHub App credentials (
githubAppID,githubAppInstallationID, private key) avoid personal tokens for whole organisations. - These Secrets themselves must be created from a vault or sealed — not committed in plain text.
- Add the Git host's SSH key with
argocd cert add-ssh(orssh_known_hostsinargocd-ssh-known-hosts-cm) instead of disabling host checking.
Rule: Argo CD should be able to read Git, and Git should never be able to read a secret.