Skip to main content
Cloud DevOps Solutions
Get a quote Talk to an engineer

GitOps / Helm / ArgoCD

Secrets in GitOps: Sealed Secrets or External Secrets Operator

GitOps puts everything in git, but secrets must never be committed in plain text. Sealed Secrets encrypts them so ciphertext can live in git. External Secrets Operator keeps them in a cloud vault and syncs them into the cluster. We help you choose, install and operate one.

Key takeaways

  • Base64 in a Kubernetes Secret is encoding, not encryption, and must never be committed.
  • Sealed Secrets is simple and self-contained; External Secrets Operator centralises secrets in a vault and supports rotation.
  • Most teams on AWS, Azure or GCP are better served by External Secrets Operator with their native secret manager.
  • Back up the Sealed Secrets controller key or lose the ability to decrypt after a rebuild.
  • Whichever tool you choose, restrict who can read Secret objects in the cluster too.

The contradiction at the heart of GitOps

GitOps says everything that runs in the cluster should be described in git. Security says passwords must never be in git. Both are right, and the way through is to store something in git that is safe to read: an encrypted blob, or a reference to a secret that lives elsewhere.

This page is how we resolve that for clients, as part of our GitOps, Helm, ArgoCD and Flux service. Background on the object itself is in our glossary entries for ConfigMaps and Secrets and Sealed Secrets.

What good looks like

  • No secret value appears in git history, CI logs or chat.
  • Secrets are rotated on a schedule without a release.
  • Access to read a secret is audited and restricted.
  • A rebuilt cluster recovers all its secrets from git plus the secret store, without manual steps.
  • Developers can add a new secret without learning cryptography.

Two main approaches

Sealed Secrets

A controller in the cluster holds a private key. You encrypt a Secret locally with kubeseal using the cluster’s public certificate, producing a SealedSecret that is safe to commit. Only the controller can decrypt it.

kubectl create secret generic db-credentials \
  --from-literal=username=app \
  --from-literal=password="$(openssl rand -base64 24)" \
  --dry-run=client -o yaml \
  | kubeseal --format yaml > sealed-db-credentials.yaml

Commit sealed-db-credentials.yaml. The controller turns it into a normal Secret in the target namespace. Sealed Secrets is scoped by name and namespace by default, so a sealed value cannot be moved elsewhere.

Strengths: simple, no external dependency, works anywhere, fully offline. Weaknesses: the secret’s source of truth is the cluster key, not a vault. Rotation is manual, there is no central view of who uses what, and the key must be backed up.

External Secrets Operator

The operator reads a value from a secret manager and writes a Kubernetes Secret. Git contains only the reference:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-credentials
  namespace: payments
spec:
  refreshInterval: 1h
  secretStoreRef:
    kind: ClusterSecretStore
    name: aws-secrets-manager
  target:
    name: db-credentials
  data:
    - secretKey: password
      remoteRef:
        key: prod/payments/db
        property: password

Check which API version your installed release serves, since it has moved between versions. The store is configured once with workload identity, so no access keys are stored.

Strengths: one source of truth, native rotation and audit from AWS Secrets Manager, Azure Key Vault or HashiCorp Vault, and multiple clusters can share the same secret. Weaknesses: a runtime dependency on the vault, and an extra operator to run.

Choosing between them

SituationOur usual recommendation
Single small cluster, no existing vaultSealed Secrets
AWS, Azure or GCP with a native secret managerExternal Secrets Operator
Many clusters sharing secretsExternal Secrets Operator
Flux-centric team already using age or KMSSOPS with Flux native decryption
Strict audit and rotation requirementsExternal Secrets Operator with a managed vault

Flux has built-in SOPS decryption for Kustomizations, which is a solid third option: files are encrypted in git with a KMS or age key, and the controller decrypts at apply time. ArgoCD does not decrypt natively, so teams there tend towards Sealed Secrets or External Secrets Operator.

Operating it well

Identity, not keys

Give the operator access through IAM Roles for Service Accounts on EKS, or the equivalent on AKS and GKE. Scope the policy to a path prefix, such as secrets under prod/payments/*, so a compromise cannot read everything.

Access inside the cluster

Encrypting in git is only half the story. Anyone who can get secrets in a namespace can read the values. We tighten this with Kubernetes RBAC and, for GitOps users, with the access model described here. Developers can deploy without ever reading production secrets.

Rotation and reloads

Changing a secret is not the same as an application noticing. Mounted secret volumes update eventually, but environment variables are only read at startup. We pair secrets with a reloader or a checksum annotation that triggers a rolling restart, and we test rotation end to end before relying on it.

Decisions and trade-offs

Vault dependency. External Secrets Operator makes your cluster rely on the vault’s availability at sync time. Existing Kubernetes Secrets keep working if the vault is unavailable, but new ones cannot be created. We account for that in recovery plans.

Where the key lives. For Sealed Secrets, the private key is your crown jewel. Back it up in a secure store and rehearse recovery.

When not to use either. If a workload can fetch secrets itself, directly from a cloud secret manager using workload identity, it may not need Kubernetes Secrets at all. That removes a layer, at the cost of changing the application.

A worked example: rotating a database password

Take a payments database password. With External Secrets, rotation happens in the cloud secret store, either by hand or by an automated rotation function. The operator refreshes the Kubernetes Secret on its interval, and the application picks up the new value after a reload or restart. Nothing in git changes, and the audit record lives in the secret store.

With Sealed Secrets, rotation means generating the new value, sealing it for the target cluster and namespace, and merging the encrypted file. That is more manual, but it works without any external service, which suits small estates and air-gapped clusters. Whichever you choose, we test the rotation end to end before go-live, including the awkward case where the old and new value must both work for a short overlap.

Common failure modes

  • Plain secrets already in history. Rotate them; deleting the file does not remove the history.
  • Lost controller key. Fixed with backup and a tested restore.
  • Overbroad vault permissions. One compromised pod reads every secret. Use path-scoped policies.
  • Secrets in Helm values. They end up in release history. Reference existing Secrets instead, as we do in our chart designs.
  • Rotation never tested. The first real rotation becomes an outage.

Delivery and handover

We deliver the controller installed through GitOps, store definitions, example manifests for each secret type, a rotation procedure, a key backup process where relevant, and a migration of your existing secrets with the old credentials rotated. Your team creates and rotates a secret themselves before we finish.

How to tell it is working

A repository scan finds no secrets. A new cluster comes up with every secret restored. Rotation happens by changing one value. Access to secrets is narrow and logged. If you want to check your current setup, contact us.

References

How we deliver it

  1. 01

    Inventory secrets

    We find where secrets live now: manifests, CI variables, scripts, wikis and chat, and who has access.

  2. 02

    Choose the model

    Sealed Secrets, External Secrets Operator, SOPS, or a combination, selected for your cloud, team size and rotation needs.

  3. 03

    Install and configure

    The controller is deployed through GitOps, with identity to your vault configured without long-lived keys where possible.

  4. 04

    Migrate secrets

    Existing secrets are moved into the new mechanism one application at a time, and old copies are rotated.

  5. 05

    Set rotation and access

    Rotation schedules, refresh intervals and least-privilege access to the vault and to Secret objects are defined.

  6. 06

    Test recovery

    We rebuild a cluster from git to confirm secrets are restored, and document key backup and break-glass steps.

Tools and technologies

Sealed SecretskubesealExternal Secrets OperatorAWS Secrets ManagerAzure Key VaultGoogle Secret ManagerHashiCorp VaultSOPSageIRSAWorkload Identity

Frequently asked questions

Can I store Kubernetes Secrets in git?
Not as plain Secret manifests. Their values are only base64 encoded, so anyone with repository access can read them. You can safely store encrypted forms: a SealedSecret that only your cluster can decrypt, a SOPS-encrypted file, or an ExternalSecret that references a value held in a vault. The ciphertext or reference goes in git.
Sealed Secrets or External Secrets Operator?
Sealed Secrets is self-contained and ideal for small teams without an existing vault. External Secrets Operator pulls from AWS Secrets Manager, Azure Key Vault, Vault and others, so secrets are managed in one place with audit and rotation. If you already use a cloud secret manager, External Secrets Operator is usually the better fit.
What happens if we lose the Sealed Secrets key?
New clusters cannot decrypt existing SealedSecret objects, so you would need to recreate every secret. The controller generates a key pair and renews it periodically. Back up the private keys securely outside the cluster and test restoring them. Alternatively, choose a vault-backed approach where secrets are not tied to a cluster key.
How does External Secrets Operator authenticate to the cloud?
Preferably with workload identity: IAM Roles for Service Accounts on EKS, Workload Identity on GKE or AKS. The operator receives short-lived credentials bound to a service account, so no access keys are stored in the cluster. Avoid static keys in a Kubernetes Secret, which recreates the problem you are solving.
How do we rotate secrets without downtime?
With External Secrets Operator, update the value in the vault and the operator refreshes the Kubernetes Secret on its interval. Applications must pick the change up, either by reading mounted files that update or by a rolling restart triggered by a reloader tool. Test the full path, because the sync is rarely the part that fails.

Talk to an engineer about sealed secrets vs external secrets operator

Book a free 30-minute call. We review your current setup and tell you honestly whether and how we can help.

Written by Saddique Khan, DevOps / SRE / Cloud Consultant Engineer. Last updated 2026-10-08.