Skip to content
Security

Sealed Secrets vs External Secrets Operator vs SOPS

Three GitOps secret patterns for Kubernetes. Sealed Secrets for pure GitOps, ESO for upstream-store sync, SOPS for Helm-heavy workflows. Decision matrix.

A
Abhishek Patel11 min read

Infrastructure engineer with 10+ years building production systems on AWS, GCP,…

Sealed Secrets vs External Secrets Operator vs SOPS
Sealed Secrets vs External Secrets Operator vs SOPS

Sealed Secrets vs External Secrets Operator vs SOPS: Quick Verdict

Three dominant patterns for GitOps-friendly Kubernetes secrets, each solving a different problem. Sealed Secrets encrypts the secret in your Git repo with a cluster-specific key; the SealedSecret CRD gets decrypted by an in-cluster controller into a native K8s Secret. Pure GitOps with no external store. External Secrets Operator (ESO) doesn't store secrets at all — it syncs them from upstream stores (Vault, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, Doppler) into K8s Secrets on a polling interval. Source of truth lives elsewhere. SOPS encrypts files at rest; Helm or Kustomize decrypts them at apply time. The honest picks: Sealed Secrets for pure GitOps with no external store, ESO when you already have a cloud secret manager as source of truth, SOPS for Helm-heavy workflows where the encryption-at-the-file-level model fits.

ToolArchitectureSource of truthRotation storyBest for
Sealed SecretsK8s controller + CRDGit (encrypted)Manual re-seal + commitPure GitOps, no external store
External Secrets OperatorK8s controller polling upstreamUpstream store (Vault, AWS, GCP, etc.)Automatic via upstream rotationExisting secret manager in place
SOPSFile-level encryptionGit (encrypted), KMS for keysManual re-encrypt + commitHelm / Kustomize / Terraform-heavy stacks

Last updated: April 2026 — verified against Sealed Secrets 0.27, ESO 0.10.x, SOPS 3.10. Integration paths with ArgoCD, Flux, Helm, and Kustomize tested.

Sealed Secrets: GitOps Without an External Store

Sealed Secrets (originally from Bitnami) is the simplest of the three. The flow:

  1. You install the Sealed Secrets controller in your cluster — it generates a public/private keypair, the public key is exposed.
  2. You write a Secret manifest, then run kubeseal against it with the public key. Output: a SealedSecret custom resource with encrypted ciphertext.
  3. You commit the SealedSecret to Git. Anyone can read it; only the cluster's controller (with the private key) can decrypt.
  4. When the controller sees a SealedSecret in the cluster, it decrypts it into a native K8s Secret, which pods consume normally.
# Encrypt a secret for the cluster
kubectl create secret generic db-creds \
    --from-literal=password='supersecret' \
    --dry-run=client -o yaml | \
  kubeseal -o yaml > db-creds-sealed.yaml

# Commit the sealed file to Git
git add db-creds-sealed.yaml
git commit -m "Add db creds for production"

Strengths

  • Pure GitOps: secrets live in Git like everything else, no external runtime service to operate
  • Cluster-specific encryption: leaked sealed secrets can't be decrypted by a different cluster
  • Tiny operational footprint: a single controller pod, no external dependencies

Weaknesses

  • Rotation is manual: changing a secret means re-encrypting locally and pushing a new commit
  • Cluster key rotation is painful: if you re-key the controller, every existing SealedSecret needs to be re-sealed against the new key
  • No multi-cluster sharing: a SealedSecret encrypted for cluster A can't be decrypted by cluster B (this is by design but limits patterns)
  • No audit trail beyond Git history: which application accessed which secret? Git doesn't tell you that

External Secrets Operator: Sync from Upstream Stores

ESO inverts the model: secrets don't live in your repo at all. The source of truth is an external store (HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, Doppler, Akeyless, IBM Secrets Manager). ESO runs as a controller that polls the upstream store on an interval and syncs values into native K8s Secrets.

# Upstream provider config
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: aws-secrets-store
spec:
  provider:
    aws:
      service: SecretsManager
      region: us-east-1
      auth:
        jwt:
          serviceAccountRef:
            name: external-secrets-sa
---
# Pull a specific secret
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-creds
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: aws-secrets-store
    kind: SecretStore
  target:
    name: db-creds
    creationPolicy: Owner
  data:
    - secretKey: password
      remoteRef:
        key: production/db
        property: password

Strengths

  • Single source of truth: you already have AWS Secrets Manager / Vault / etc. — ESO bridges into K8s without duplication
  • Automatic rotation propagation: rotate at the upstream store, ESO syncs to K8s within the refresh interval (default 1h)
  • Multi-cluster fan-out: same upstream secret syncs to many clusters; sealed secrets can't do this
  • Real audit trail: upstream store logs every read; you know which cluster pulled which secret

Weaknesses

  • Adds operational complexity: you're now operating ESO + the upstream store + auth between them
  • Refresh interval lag: secret rotated upstream takes up to refreshInterval to propagate. Default 1h; tune lower for faster propagation, higher for less load
  • Auth between cluster and store: IRSA on EKS, Workload Identity on GKE, AppRole for Vault — each integration has its own gotchas
  • Not pure GitOps: the actual secret value lives outside Git; only the ExternalSecret manifest pointing at it is in Git

The Vault vs AWS Secrets Manager comparison covers picking the upstream store ESO syncs from. The broader alternatives roundup covers all 8 options ESO supports.

SOPS: File-Level Encryption

SOPS (Secrets OPerationS, originally from Mozilla) takes a fundamentally different approach: encrypt files in place. Instead of encrypting whole files, SOPS encrypts only the values, leaving keys readable. This makes git diffs of SOPS-encrypted files surprisingly useful — you can see which key changed even when you can't decrypt the values.

# db-creds.enc.yaml (after SOPS encryption)
apiVersion: v1
kind: Secret
metadata:
  name: db-creds
stringData:
  username: ENC[AES256_GCM,data:7a3...,iv:abc...,tag:xyz...,type:str]
  password: ENC[AES256_GCM,data:9d7...,iv:def...,tag:uvw...,type:str]
sops:
  kms:
    - arn: arn:aws:kms:us-east-1:123456:key/abc-def
      created_at: "2026-04-01T10:00:00Z"
  encrypted_regex: ^(data|stringData)$

Decryption integration

SOPS integrates with Helm via plugins, with Kustomize via the ksops plugin, with Terraform via the sops provider, and with raw kubectl via sops -d | kubectl apply. The encryption keys can be KMS (AWS, GCP, Azure), age (GPG-alternative), GPG, or plain PGP. Most teams use AWS KMS or age in 2026.

Strengths

  • Works beyond Kubernetes: any deployment system that can run a decryption step works (Helm, Kustomize, Terraform, Ansible, plain shell scripts)
  • Diffable encrypted files: keys are visible, only values are encrypted; you see the structural diff in PRs
  • Multi-recipient encryption: encrypt once, decrypt with multiple keys (different team members, different KMS keys)
  • Mature ecosystem: SOPS has been around since 2017; integrations exist for nearly every tool

Weaknesses

  • Manual rotation: same as Sealed Secrets — rotate by re-encrypting and committing
  • Key management is your problem: KMS access, age key rotation, GPG key management — none are trivial
  • Per-environment complexity: typically you have prod-keys and staging-keys; managing access correctly is harder than it sounds
  • Breaks "single command apply" flows: you need a wrapper that decrypts before apply; not all CI systems support that cleanly

Decision Matrix: Pick X If...

  • Pick Sealed Secrets if: you're 100% on Kubernetes, you want secrets in Git as source of truth, you don't already run a secrets manager, and your rotation cadence is low (quarterly or less).
  • Pick ESO if: you already have a cloud secrets manager (AWS, GCP, Azure, Vault, Doppler) as source of truth, you need automatic rotation propagation across multiple clusters, or you have audit/compliance requirements that demand external store logging.
  • Pick SOPS if: your stack is Helm-heavy or extends beyond Kubernetes (Terraform, Ansible-managed servers, raw shell deploys), or you specifically need diffable encrypted files in PRs.
  • Use both Sealed Secrets and ESO: rare but valid pattern — Sealed Secrets for cluster-bootstrap secrets (the secrets needed before ESO's auth works), ESO for everything else after the cluster is healthy.

Multi-Cluster Patterns

PatternSealed SecretsESOSOPS
Same secret in 5 clustersRe-seal 5 times (5 different cluster keys)One ExternalSecret per cluster pointing same upstreamRe-encrypt for 5 KMS keys (or shared key)
Per-tenant secret in 50 clusters50 sealed manifests in Git50 ExternalSecrets, single upstream entry per tenant50 SOPS files
Auto rotation across all clustersManual: re-seal + commit triggers redeployAutomatic: rotate upstream, ESO syncs in refreshIntervalManual: re-encrypt + commit

For multi-cluster fleets at scale (10+ clusters), ESO is dramatically less work than Sealed Secrets or SOPS. The trade-off: you're now operating an upstream store, which might be why you weren't using one in the first place. ArgoCD and FluxCD both integrate with all three patterns.

Common Pitfalls

  1. Sealed Secrets controller key loss: if you lose the controller's private key without backing it up, every existing SealedSecret in Git becomes undecryptable. Back up the master key (kubectl get secret -n kube-system sealed-secrets-key) to a secure offline store.
  2. ESO refreshInterval too tight: setting refreshInterval: 30s hammers your upstream store API. Default 1h is fine for 99% of use cases; only tune lower if you have latency-sensitive secret rotation needs.
  3. SOPS without committing the unencrypted .sops.yaml: SOPS reads the .sops.yaml config to know what keys to encrypt with. That file MUST be in Git. The encrypted output files MUST also be in Git. The unencrypted source file MUST NOT be.
  4. Forgetting CRD upgrades: Sealed Secrets and ESO both ship CRDs. Helm chart upgrades sometimes don't update the CRDs (Helm's deliberate behavior). Manual CRD upgrade is required: kubectl apply --force-conflicts -f path/to/crds.yaml.
  5. Mixing patterns inconsistently: SealedSecret for some, ExternalSecret for others, SOPS for others. The cognitive cost of "where does this secret live?" eats engineering hours. Pick one pattern and stick with it for new work.

Pro tip: For most teams in 2026, ESO is the right default if you can stand up a cloud-native secret manager. The operational overhead of "yet another upstream store" is offset by automatic rotation propagation, multi-cluster sharing, and proper audit trails. Sealed Secrets is the right pick when you specifically don't want an external store. SOPS is the right pick when your stack genuinely extends beyond Kubernetes. The advanced multi-tenant secret patterns I deploy in production I send to the newsletter.

Frequently Asked Questions

What's the difference between Sealed Secrets and External Secrets Operator?

Sealed Secrets stores encrypted secrets in Git, decrypted by an in-cluster controller — pure GitOps with no external store. External Secrets Operator (ESO) syncs secrets from an upstream store (AWS Secrets Manager, Vault, etc.) into K8s, with the upstream as source of truth. Sealed Secrets needs no external service; ESO needs both the operator and the upstream store. Pick Sealed Secrets for pure GitOps; pick ESO when you already have a secret manager.

Should I use SOPS or Sealed Secrets?

SOPS works beyond Kubernetes (Helm, Terraform, Ansible) and produces diffable encrypted files that show structural changes in PRs. Sealed Secrets is Kubernetes-only but simpler to set up — single controller, single CRD. Pick SOPS for Helm-heavy or multi-tool workflows; pick Sealed Secrets for pure-Kubernetes deployments where you want minimal operational overhead.

Is External Secrets Operator GitOps-compatible?

Yes, but in a different way than Sealed Secrets. The ExternalSecret manifest (which points at upstream secrets) is in Git; the actual secret values live in the upstream store. Some teams call this "indirect GitOps" since the source-of-truth value is outside Git. ArgoCD and Flux both support ESO patterns natively.

What is kubeseal?

kubeseal is the CLI for Sealed Secrets. It takes a regular Kubernetes Secret manifest and encrypts it against the cluster's public key, producing a SealedSecret manifest you can commit to Git. The cluster controller decrypts it back into a Secret. kubeseal also has commands for re-sealing (after key rotation), validation, and key rotation operations.

Can I use SOPS with Helm?

Yes, via the helm-secrets plugin or by piping sops -d to helm install. The plugin integrates SOPS decryption into the Helm install/upgrade lifecycle automatically. Encrypted values files (typically named secrets.enc.yaml or secrets.yaml) live in Git; the plugin decrypts them before passing to Helm. This is one of the most common SOPS use cases in 2026.

Does External Secrets Operator support Vault?

Yes — ESO supports HashiCorp Vault as a SecretStore provider with multiple auth methods (Kubernetes service account, AppRole, JWT, token). Vault stays the source of truth; ESO syncs into K8s Secrets on a polling interval. This combination gives you Vault's dynamic secrets capability with K8s-native consumption pattern. ESO also supports AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, Doppler, Akeyless, and 15+ other backends.

What happens if Sealed Secrets controller crashes?

Existing decrypted Secrets in the cluster keep working — they're already native K8s Secrets. New SealedSecret manifests won't decrypt until the controller comes back. ArgoCD or Flux will retry on the next reconciliation. Mitigation: run multiple controller replicas (the controller supports HA mode) and monitor its health like any other critical workload.

Pick One Pattern, Stick with It

The biggest mistake teams make with GitOps secrets is mixing patterns inconsistently — Sealed Secrets for some, ESO for others, raw plaintext for "just-this-once." Pick the pattern that matches your constraints and apply it everywhere. Sealed Secrets when you want pure GitOps with no external store. ESO when you already operate a cloud secret manager. SOPS when your stack genuinely extends beyond Kubernetes. The right choice depends on whether you have an existing upstream store, your multi-cluster scale, and how often you rotate secrets.

A

Written by

Abhishek Patel

Infrastructure engineer with 10+ years building production systems on AWS, GCP, and bare metal. Writes practical guides on cloud architecture, containers, networking, and Linux for developers who want to understand how things actually work under the hood.

Related Articles

Enjoyed this article?

Get more like this in your inbox. No spam, unsubscribe anytime.

Comments

Loading comments...

Leave a comment

Stay in the loop

New articles delivered to your inbox. No spam.