Skip to main content
In this lesson we encrypt Kubernetes Secrets with Bitnami Sealed Secrets so they can be safely stored in Git and only be decrypted by the Sealed Secrets controller running in the target cluster. We will work inside the sealed-secret directory of the cgoa-demos repository.
The image shows a GitHub repository interface with files and folders listed, including updates and commit details. It highlights the use of the Smarty language.
In that directory you can see a Deployment, a Service, and a standard Kubernetes Secret file that was recently updated.
The image shows a file directory from a web-based code repository, featuring a folder called "sealed-secrets" with YAML files related to deployment and services. The repository interface displays recent updates, including modifications to a "secret.yml" file.
Important: a Kubernetes Secret stores values Base64-encoded, not encrypted. Anyone with the file can decode the values. Example of a plain Secret YAML (values are Base64-encoded):
Because GitOps workflows keep manifests in Git, never commit plain (or only Base64-encoded) Secrets. Instead, encrypt secrets for storage in Git with a tool that only the cluster controller can decrypt — for example, Bitnami Sealed Secrets. Overview — how this works
  • The Sealed Secrets controller in the cluster holds a private key.
  • You fetch the controller’s public certificate and use kubeseal to encrypt a regular Secret YAML into a SealedSecret.
  • The SealedSecret is safe to commit to Git because it contains only encrypted data.
  • When applied to a cluster, the controller decrypts it and creates a standard Kubernetes Secret for pods to consume.
Step 1 — get the Sealed Secrets controller public certificate The kubeseal client needs the controller’s public cert to encrypt secrets. Fetch it from the namespace where the controller is installed (commonly kube-system):
  1. List secrets in the controller namespace:
  1. Extract and decode the controller certificate (replace <SECRET_NAME> with the secret name you found):
Notes:
  • The bracket notation ["tls.crt"] is required because the key contains a dot.
  • ss-public.crt will contain the controller public certificate in PEM format, e.g.:
The image shows a terminal window in a code editor displaying encoded or encrypted text.
Step 2 — generate the Secret YAML locally (without applying it) Use kubectl create secret with a dry-run to produce the Secret YAML file:
Tip: quote literal values that include shell-sensitive characters (like $) to prevent expansion, as shown for the password. Generated Secret (simplified):
Step 3 — encrypt the Secret with kubeseal Use the public certificate and choose a scope for the resulting SealedSecret (cluster-wide or namespace):
This reads the cleartext Secret from stdin and writes a SealedSecret resource to sealed-secret.yaml. A SealedSecret uses spec.encryptedData (not data) and is safe to commit. Example (truncated):
Do not commit plain Secret YAML files to Git. Commit only the SealedSecret YAML so secrets remain encrypted in the repository.
Step 4 — commit the SealedSecret to Git and deploy with Argo CD
  • Commit sealed-secret.yaml to the repository path monitored by Argo CD.
  • Configure an Argo CD Application pointing at the repo/path containing the SealedSecret (you can create the application’s namespace via Argo CD or beforehand).
  • Sync the Argo CD Application.
When Argo CD applies the SealedSecret, the Sealed Secrets controller intercepts it, decrypts the encryptedData with its private key, and creates a regular Kubernetes Secret in the target namespace for your application. Argo CD UI — application view
The image shows a user interface of Argo CD, displaying a list of applications with their synchronization and health statuses. It includes details for each application, like repository, target revision, and last sync time.
You can inspect application relationships and sync/health status in Argo CD.
The image shows the interface of Argo CD, depicting the application status and a visual representation of application resources and their relationships. It indicates a "Healthy" app health and a "Synced" sync status.
Step 5 — verify the Secret in the cluster
  1. Verify namespaces (you should see secret-app if Argo CD created it):
  1. Check resources in the application namespace:
  1. Inspect the created Secret (the Sealed Secrets controller creates a normal Secret resource — values are still Base64-encoded per Kubernetes format, but represent the decrypted values from the original Secret):
Example output (truncated):
The image shows a screenshot of a Bitnami interface displaying encrypted credentials such as username, password, and API key, noting they are managed with Bitnami Sealed Secrets.
Key takeaways
  • SealedSecrets are safe to commit to Git because they store only encrypted data.
  • The cluster’s Sealed Secrets controller holds the private key and will decrypt SealedSecrets to create real Kubernetes Secrets in the target namespace.
  • Applications consume the resulting Secret as usual.
Quick command reference Alternatives and further reading This concludes the demo.

Watch Video

Practice Lab