Skip to content

Sealed Secrets

No plaintext secret is ever committed to Git. Secrets are encrypted with Sealed Secrets before they go into a repo; only the matching in-cluster controller holds the private key that can decrypt them, so the sealed value is safe to store in a public repository.

One controller per layer

Each layer runs its own controller, so one layer's key can never decrypt another layer's secrets:

Controller Namespace Seals for
sealed-secrets-platform platform-shared platform-layer namespaces
sealed-secrets-addons addons-shared addons-layer namespaces
sealed-secrets-opendesk opendesk-addons openDesk namespaces

Sealing a value

Pick the controller for the layer whose namespace you're sealing into, and use strict scope โ€” the ciphertext is bound to that exact namespace and secret name:

printf '%s' 'the-secret-value' | kubeseal --raw \
  --namespace <target-namespace> --name <secret-name> --scope strict \
  --controller-name <controller> --controller-namespace <controller-namespace>

Paste the resulting ciphertext into a SealedSecret manifest under the right key. Commit โ€” ArgoCD applies the SealedSecret, and the controller decrypts it into a real Secret in the cluster.

Rules of the road

  • --scope strict: the sealed value only works for that namespace + secret name. Renaming or moving it means re-sealing.
  • Run the pipe locally โ€” the plaintext is piped straight into kubeseal and never leaves your workstation or lands in a file.
  • The controller's private key is the crown jewel. It's backed up out-of-band, never in Git. Restore it and every existing SealedSecret still decrypts; lose it and they must all be re-sealed.

See also: Architecture ยท Operations.