
September 10, 2026
11 min read
By Kokil Thapa | Last reviewed: September 2026
You cannot store plain database passwords or API keys in a Git repository and still call your pipeline secure. GitOps demands that cluster state lives in version control, yet Kubernetes Secrets are only base64-encoded—not encrypted. To manage secrets in GitOps with Sealed Secrets, you encrypt sensitive values client-side so Git holds ciphertext only, and a cluster controller decrypts them into native Secrets at sync time. This guide walks through installation, daily workflows, comparisons with alternatives, and production patterns I've used alongside GitOps principles on real deployments.
Why do you need Sealed Secrets in a GitOps workflow?
GitOps treats Git as the single source of truth. Argo CD, Flux, or similar tools continuously reconcile cluster state from committed manifests. That model breaks the moment someone commits a literal DB_PASSWORD string. Base64 in a Kubernetes Secret is not encryption. Anyone with repo access reads production credentials in seconds.
Teams often react by keeping secrets out of Git entirely. They maintain parallel stores—CI variables, cloud vaults, or manual kubectl steps. Drift follows. The cluster no longer matches the repo. Rollbacks skip credential updates. New environments need hand-copying values nobody documented.
Sealed Secrets closes that gap. You commit encrypted SealedSecret resources. They are safe in public forks, pull-request diffs, and long-lived branches. Only the target cluster's private key decrypts them. Your Argo CD GitOps pipeline stays fully declarative without exposing plaintext.
On production systems I maintain, pairing Sealed Secrets with secrets scanning in Git and CI gives defence in depth. Sealed Secrets prevents accidental plaintext commits. Gitleaks catches mistakes when someone bypasses the workflow.
How does the Sealed Secrets encryption model work?
Bitnami Sealed Secrets uses asymmetric cryptography. The controller generates an RSA key pair on first startup. It exposes the public key through a cluster endpoint and keeps the private key inside the cluster—typically in a Secret mounted into the controller pod.
The kubeseal CLI fetches that public key. It encrypts your Secret data into a SealedSecret custom resource. Once sealed, ciphertext binds to namespace and name scope. You cannot rename the resource or move it to another namespace without re-sealing.
Core components you will deploy
- Controller: Watches
SealedSecretobjects and emits standardSecretresources. - CRD: Defines the
SealedSecretAPI atbitnami.com/v1alpha1. - kubeseal CLI: Local encryption tool used by developers and CI jobs.
- cert-manager integration: Optional automatic key rotation in larger setups.
Official documentation for the project lives at the Bitnami Sealed Secrets GitHub repository. Kubernetes native Secret behaviour is documented in the Kubernetes Secrets reference.
How do you install and configure Sealed Secrets on Kubernetes?
Install the controller once per cluster—or per trust boundary if clusters must not share keys. Most teams use the upstream Helm chart or the static manifest bundle from the project releases page.
Install the controller with Helm
helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
helm repo update
helm install sealed-secrets sealed-secrets/sealed-secrets \
--namespace kube-system \
--create-namespace \
--set fullnameOverride=sealed-secrets-controller Verify the controller pod runs and the CRD exists:
kubectl get pods -n kube-system -l app.kubernetes.io/name=sealed-secrets
kubectl get crd sealedsecrets.bitnami.com Install the kubeseal CLI locally
Match CLI version to controller version. Version skew often causes opaque encryption errors.
KUBESEAL_VERSION='0.27.1'
curl -OL "https://github.com/bitnami-labs/sealed-secrets/releases/download/v${KUBESEAL_VERSION}/kubeseal-${KUBESEAL_VERSION}-linux-amd64.tar.gz"
tar xfz kubeseal-${KUBESEAL_VERSION}-linux-amd64.tar.gz kubeseal
sudo install kubeseal /usr/local/bin/kubeseal Fetch the cluster public certificate
Store the public cert in your repo for offline sealing in CI. This cert encrypts only—it cannot decrypt.
kubeseal --fetch-cert \
--controller-name=sealed-secrets-controller \
--controller-namespace=kube-system \
> pub-cert.pem For multi-cluster GitOps layouts, each environment gets its own cert file. A production SealedSecret sealed with a staging key fails on the production cluster by design. That failure mode protects you from cross-environment leaks. Patterns like this appear in multi-cluster GitOps patterns guides.
How do you create and commit sealed secrets safely?
The daily workflow stays close to standard kubectl. You write a normal Secret manifest, pipe it through kubeseal, and commit the output. Never commit the plaintext intermediate file.
Seal a secret from a literal value
- Create a temporary local Secret manifest—add the filename to
.gitignore. - Pipe it through kubeseal to produce a SealedSecret YAML.
- Commit only the SealedSecret into your GitOps repo path.
- Let Argo CD or Flux sync; confirm the native Secret exists.
kubectl create secret generic db-credentials \
--from-literal=username=app_user \
--from-literal=password='S3cur3P@ss!' \
--namespace=production \
--dry-run=client -o yaml > /tmp/db-credentials.yaml
kubeseal \
--controller-name=sealed-secrets-controller \
--controller-namespace=kube-system \
--format yaml \
< /tmp/db-credentials.yaml \
> clusters/production/sealed/db-credentials.yaml
shred -u /tmp/db-credentials.yaml Commit the sealed output:
git add clusters/production/sealed/db-credentials.yaml
git commit -m "Add sealed DB credentials for production"
git push origin main Reference the resulting Secret from a Deployment as usual:
env:
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password Rotate a sealed secret without downtime
Update the plaintext locally, re-seal with the same name and namespace, commit, and sync. The controller updates the native Secret. Restart pods if they do not reload env vars automatically.
kubectl create secret generic api-token \
--from-literal=token='new-token-value' \
--namespace=production \
--dry-run=client -o yaml | kubeseal --format yaml \
> clusters/production/sealed/api-token.yaml Generate strong random values before sealing using a local password generator or openssl rand -base64 32. For base64-encoded binary keys, a base64 encoder tool helps verify format before you seal.
How do Sealed Secrets compare to other GitOps secret tools?
Sealed Secrets is one option—not the only one. Pick based on team size, cloud footprint, rotation needs, and audit requirements.
| Tool | Secrets in Git | External dependency | Best fit |
|---|---|---|---|
| Sealed Secrets | Yes—encrypted SealedSecret CRs | None beyond the cluster controller | Small teams, self-hosted K8s, simple GitOps |
| Mozilla SOPS | Yes—encrypted YAML/JSON files | KMS, PGP, or age keys | Multi-resource repos, Terraform + K8s mixed repos |
| External Secrets Operator | No—references only | Vault, AWS Secrets Manager, GCP SM | Enterprise audit, dynamic secrets, central vault |
| Cloud-native sync | No | AWS/GCP/Azure secret stores | Heavy cloud IAM integration |
Sealed Secrets wins on simplicity. No vault cluster to operate. No cloud lock-in. Encrypted manifests diff cleanly in pull requests. The trade-off is key management inside the cluster. Backup the controller private key securely. Losing it means re-sealing every secret.
For vault-centric setups, read External Secrets Operator with Vault and HashiCorp Vault secrets management. For Ansible-heavy infra repos, Ansible Vault for secrets solves a parallel problem outside Kubernetes.
What are the best practices for production Sealed Secrets?
Treating Sealed Secrets as fire-and-forget creates incidents. These practices come from production GitOps work alongside CI/CD secrets management best practices.
Scope encryption per cluster and namespace
Use strict scope unless you have a documented reason for cluster-wide secrets. Strict scope binds ciphertext to both namespace and name. A sealed staging credential cannot decrypt in production even if someone commits it to the wrong folder.
kubeseal --scope strict --format yaml < secret.yaml > sealed-secret.yaml Backup the controller private key
Export and store the sealing key in your organisation password vault. After disaster recovery on a fresh cluster, restore the key before syncing SealedSecrets. Without it, every secret needs manual re-encryption.
kubectl get secret -n kube-system \
-l sealedsecrets.bitnami.com/sealed-secrets-key=active \
-o yaml > sealed-secrets-master-key-backup.yaml Encrypt that backup file itself. Never commit it to Git plain.
Integrate with Argo CD or Flux cleanly
Place SealedSecret manifests in the same Kustomize overlay or Helm chart path as the Deployments that consume them. Argo CD sync waves can order SealedSecret before Deployment if needed. See set up GitOps with Argo CD and Flux GitOps toolkit deep dive for repo layout examples.
Restrict RBAC on native Secrets
Sealed Secrets protects Git. Inside the cluster, native Secrets still need RBAC. Limit who can kubectl get secret in production namespaces. Enable encryption at rest via Kubernetes API encryption provider config for etcd.
Automate sealing in CI with care
CI jobs can seal secrets using the public cert—no cluster admin kubeconfig required. Pass plaintext via protected CI variables only. Output sealed YAML as an artefact or direct commit. Follow patterns from handle secrets in CI/CD pipelines safely.
On Laravel or API workloads deployed to Kubernetes—common on booking platforms I have shipped—application secrets like APP_KEY, payment gateway tokens, and database URLs all flow through this same sealed manifest pattern. The application code stays unchanged; only the delivery mechanism differs from a traditional .env on a single VPS.
For teams without in-house cluster ops, Linux system administration and support and maintenance services cover controller upgrades, key rotation, and GitOps pipeline hardening. Enterprise application development engagements often include this secrets layer from day one rather than retrofitting after an audit finding.
Key Takeaways
- Sealed Secrets lets you commit encrypted credentials to Git while keeping plaintext only inside the target cluster.
- Install one controller per cluster, match kubeseal version to controller version, and store the public cert for CI sealing.
- Never commit plaintext Secret manifests—seal locally or in CI, then delete temp files and run gitleaks.
- Backup the controller private key; losing it forces re-sealing every SealedSecret in the repo.
- Use strict scope by default so ciphertext cannot decrypt in the wrong namespace or cluster.
- Pair Sealed Secrets with RBAC, etcd encryption at rest, and secrets scanning for defence in depth.
People Also Ask
Can Sealed Secrets be decrypted outside the cluster?
No. Only the controller holding the private RSA key can decrypt a SealedSecret. The public key used by kubeseal performs one-way encryption. Even repository admins with full Git access cannot recover plaintext without cluster access and RBAC permission to read the generated native Secret.
Are Sealed Secrets safe in a public GitHub repository?
Yes, for practical purposes. The encrypted blob resists offline brute-force attacks when proper key lengths are used. Treat this as safe storage in Git, not a substitute for repo access control. Combine with branch protection, required reviews, and CI secrets scanning for a complete posture.
What happens when the Sealed Secrets controller is down?
Existing native Secrets remain in etcd—running pods keep working. New or updated SealedSecrets will not reconcile until the controller recovers. GitOps sync may report healthy while secret updates stall. Monitor controller health as a critical cluster component.
Does Sealed Secrets work with Argo CD and Flux?
Yes. Both sync SealedSecret CRs like any other manifest. The controller creates standard Secrets that Deployments reference. No special Argo CD plugin is required. Ensure the controller is installed before the first sync wave containing SealedSecrets.
Ship GitOps without leaking credentials
You can manage secrets in GitOps with Sealed Secrets today without operating a separate vault cluster. Install the controller, seal with kubeseal, commit encrypted manifests, and let your existing Argo CD or Flux pipeline reconcile them. Start strict, back up your keys, and scan Git for accidental plaintext. That combination covers most small and mid-size teams until central audit demands push you toward External Secrets.
Need help wiring Sealed Secrets into a production GitOps pipeline or migrating from manual kubectl secrets? Review the client portal work in my portfolio or read more on the Kubernetes secrets management and Flux CD vs Argo CD pages. When you want hands-on setup on your cluster, contact us for a scoped engagement.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

