Skip to content

Sealed Secrets Reference: kubeseal, GitOps-Safe K8s Secrets, Key Rotation & Scopes

Sealed Secrets is a Bitnami/CNCF project for storing encrypted Kubernetes Secrets safely in Git. The kubeseal CLI encrypts a Secret with the cluster’s public key — only the SealedSecrets controller in that cluster can decrypt it. The encrypted SealedSecret can live in your Git repo without any secrets leaking.

1. Sealed Secrets vs External Secrets Operator

GitOps-first vs external store — which fits your workflow
Approach Where secrets live Best for
Sealed Secrets Encrypted in Git (SealedSecret YAML) Pure GitOps teams — everything including secrets lives in Git
External Secrets Operator Vault/AWS SM/GCP/Azure (synced to K8s) Teams with existing centralised secret store
Vault Agent HashiCorp Vault (injected as files) Short-lived dynamic credentials per pod
# Install SealedSecrets controller:
helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
helm install sealed-secrets sealed-secrets/sealed-secrets   --namespace kube-system

# Install kubeseal CLI:
brew install kubeseal       # macOS
# Or: curl -OL https://github.com/bitnami-labs/sealed-secrets/releases/latest/download/kubeseal-linux-amd64

# Verify controller is running:
kubectl get pods -n kube-system -l app.kubernetes.io/name=sealed-secrets

2. Creating SealedSecrets

Encrypt a secret with kubeseal — safe to commit to Git
# 1. Create a regular K8s Secret (YAML — DON'T apply this to cluster):
kubectl create secret generic my-app-secrets   --from-literal=db-password=supersecret123   --from-literal=api-key=sk_live_abcdef   --namespace production   --dry-run=client -o yaml > my-secret.yaml

# 2. Encrypt with kubeseal (fetches public key from cluster):
kubeseal --format yaml < my-secret.yaml > my-sealed-secret.yaml

# 3. Inspect the output (safe to read — encrypted values):
cat my-sealed-secret.yaml
# apiVersion: bitnami.com/v1alpha1
# kind: SealedSecret
# metadata:
#   name: my-app-secrets
#   namespace: production
# spec:
#   encryptedData:
#     db-password: AgBy3i4OJSWK+PiTySYZZA9rO43cGDEq...
#     api-key: AgCHkQSM4...

# 4. Apply to cluster (controller decrypts → creates real K8s Secret):
kubectl apply -f my-sealed-secret.yaml

# 5. Commit my-sealed-secret.yaml to Git — it's safe
git add my-sealed-secret.yaml
git commit -m "Add encrypted DB credentials for production"

# Your pods use the resulting K8s Secret normally:
envFrom:
  - secretRef:
      name: my-app-secrets   # same name as the SealedSecret

3. Scopes — Namespace & Cluster-Wide

Control where a SealedSecret can be decrypted
# Default scope: strict — SealedSecret is bound to exact name + namespace
# Cannot be renamed or moved to different namespace
kubeseal --format yaml < secret.yaml > sealed.yaml    # strict (default)

# Namespace scope: can be renamed within the same namespace
kubeseal --scope namespace-wide --format yaml < secret.yaml > sealed.yaml

# Cluster scope: can be applied to any namespace with any name
kubeseal --scope cluster-wide --format yaml < secret.yaml > sealed.yaml

# Check scope of an existing SealedSecret:
kubectl get sealedsecret my-app-secrets -n production -o yaml | grep sealedsecrets.bitnami.com/cluster-wide

# Fetch the public key offline (useful for CI — no live cluster access needed):
kubeseal --fetch-cert > my-cluster-cert.pem
# Seal offline using the cert:
kubeseal --cert my-cluster-cert.pem --format yaml < secret.yaml > sealed.yaml

4. Key Rotation & Re-encryption

Rotate encryption keys and re-seal existing secrets
# SealedSecrets automatically generates new keys every 30 days
# Old keys are retained — existing SealedSecrets still decrypt

# List encryption keys:
kubectl get secrets -n kube-system -l sealedsecrets.bitnami.com/sealed-secrets-key

# Force a new key now (for rotation after suspected compromise):
kubectl create secret generic   --dry-run=client -n kube-system   -o json sealed-secrets-keySECRET |   kubectl annotate -f - sealedsecrets.bitnami.com/sealed-secrets-key=active --local -o yaml |   kubectl apply -f -
kubectl rollout restart deployment sealed-secrets -n kube-system

# Re-encrypt all SealedSecrets with the latest key:
# (important after rotation — old keys may be retired)
# 1. Fetch new cert:
kubeseal --fetch-cert > new-cert.pem
# 2. For each SealedSecret in Git:
#    - kubectl get secret $NAME -n $NS -o yaml | kubeseal --cert new-cert.pem > new-sealed.yaml
#    - Replace old file in Git, commit, push

# View decrypted secret (to verify before rotation):
kubectl get secret my-app-secrets -n production -o jsonpath='{.data.db-password}' | base64 -d

5. GitOps Integration

ArgoCD and Flux patterns with SealedSecrets
# ArgoCD integration:
# 1. ArgoCD syncs SealedSecrets from Git (just another CRD)
# 2. SealedSecrets controller converts them to K8s Secrets
# 3. Apps reference the K8s Secrets normally
# No special ArgoCD plugin needed — it works out of the box

# Flux integration:
# Same pattern — Flux watches Git, applies SealedSecret CRDs
# SealedSecrets controller handles decryption

# Directory structure:
# k8s/
#   base/
#     deployment.yaml
#     service.yaml
#     my-sealed-secret.yaml    # encrypted, safe in Git
#   overlays/
#     production/
#       kustomization.yaml

# Workflow for secret update:
# 1. Update the secret value
kubectl create secret generic my-app-secrets   --from-literal=db-password=newpassword456   --namespace production --dry-run=client -o yaml |   kubeseal --format yaml > my-sealed-secret.yaml
# 2. Commit the updated SealedSecret
# 3. ArgoCD/Flux syncs it → controller updates the real Secret
# 4. Pods pick up the new value on restart or via Secret rotation mechanism

# Bootstrap a new cluster:
# Export old controller's key:
kubectl get secret -n kube-system -l sealedsecrets.bitnami.com/sealed-secrets-key -o yaml > key-backup.yaml
# Import to new cluster:
kubectl apply -f key-backup.yaml
# Now the new cluster can decrypt your existing Git-stored SealedSecrets

Track Sealed Secrets and Kubernetes security tool releases.
ReleaseRun monitors Kubernetes, Docker, and 13+ DevOps technologies.

Related: External Secrets Operator Reference | ArgoCD & GitOps Reference | Flux v2 Reference

🔍 Free tool: K8s YAML Security Linter — check the K8s manifests that consume your SealedSecrets for security misconfigurations.

Founded

2023 in London, UK

Contact

hello@releaserun.com