kubeseal: Encrypt a Secret into a SealedSecret
kubeseal encrypts a regular Kubernetes Secret into a SealedSecret that only the cluster controller can decrypt, so it is safe to commit to Git.
Sealed Secrets let you keep secrets in a GitOps repo. kubeseal takes a plain Secret and produces an encrypted SealedSecret resource.
What it does
kubeseal reads a Secret manifest, fetches the sealed-secrets controller public certificate, and encrypts each value with it, emitting a SealedSecret. Only the controller in that cluster holds the private key, so the output is safe to store in Git. By default sealing is scoped to the Secret's name and namespace.
Common usage
kubectl create secret generic db \
--from-literal=password="$DB_PASSWORD" \
--dry-run=client -o yaml \
| kubeseal --format yaml > sealedsecret.yamlOptions
| Flag | What it does |
|---|---|
| --format yaml|json | Output format of the SealedSecret |
| --scope strict|namespace-wide|cluster-wide | How tightly the seal is bound to name/namespace |
| --controller-name <name> | Controller deployment name (default sealed-secrets-controller) |
| --controller-namespace <ns> | Namespace of the controller (default kube-system) |
| --cert <file> | Use an offline public cert instead of querying the cluster |
In CI
A SealedSecret sealed with the default strict scope only decrypts when applied under the exact name and namespace it was sealed for; renaming it later breaks decryption. For a secret reused across namespaces, seal with --scope cluster-wide so the controller does not enforce the namespace.
Common errors in CI
"error: cannot fetch certificate: ... the server could not find the requested resource" means the --controller-name or --controller-namespace is wrong. "cannot get sealed secret service" means kubeseal cannot reach the controller; use --cert with a fetched certificate for offline sealing. Sealing succeeds but the controller logs "no key could decrypt secret" when the SealedSecret's name/namespace differ from where it was sealed.