KUBERNETES SECRETS MANAGEMENT IN 2026: ESO, SEALED SECRETS, SOPS, AND VAULT

If you are running Kubernetes in production, you have probably stared at a YAML file containing a literal database password and thought “there has to be a better way.” There is, but the number of options keeps growing and the advice you get depends entirely on who you ask. I have been through this migration myself across several clusters, and the reality is that each approach solves a different problem.

Secrets management in Kubernetes is not a solved problem — it is a series of trade-offs. The tool that works for a three-person startup on a single EKS cluster is going to be different from what a fintech running Vault across five regions needs. The proliferation of options (External Secrets Operator, Sealed Secrets, SOPS, Vault CSI, Vault Agent Injector, and the dozen other niche tools) reflects the diversity of those requirements rather than any failure to converge.

Who Is This Guide For?

You operate Kubernetes clusters, manage CI/CD pipelines, or make architectural decisions about secret infrastructure. You already understand the basics of Kubernetes Secrets but need a clear-eyed comparison of the production-grade options available in 2026. If you are evaluating which approach to standardise on, or wondering whether you should switch from one tool to another, this guide covers the decision space.

By the End of This, You Will Know

  • The five major approaches to Kubernetes secrets management and their core trade-offs
  • Which tools fit GitOps workflows, which fit dynamic secret environments, and which fit both
  • How to pick the right approach based on your team size, compliance requirements, and operational maturity
  • A working quick-start for each major option

The State of Kubernetes Secrets in 2026

The Kubernetes Secrets API (kind: Secret) stores data as base64-encoded values in etcd. That is not encryption — it is obfuscation. The API docs have been telling you this since 2016, but I still see production clusters where developers assume kubectl get secret output is somehow protected. It is not.

Three things changed the landscape in the last two years:

  1. External Secrets Operator (CNCF Sandbox, accepted July 2022) became the default choice for teams already using cloud secret managers. It now has over 40 supported providers and is installed in roughly a third of production clusters that report their stack.

  2. SOPS was donated to the CNCF as a sandbox project and adopted age as a first-class encryption backend. Its integration with Flux and ArgoCD made it the de facto standard for file-level secret encryption in GitOps pipelines.

  3. Vault Secrets Store CSI Driver (v2.x) matured alongside the Agent Injector, offering a sidecar-free path for mounting secrets. HashiCorp’s Injector vs CSI comparison outlines the differences — notably, CSI only supports Kubernetes auth, while Injector supports all auto-auth methods including lease renewal. The choice depends on your use case.

This is the landscape in 2026. Each tool has a clear use case, and the mistake I see most often is teams picking the wrong one because it happened to be the first they heard about.

Approach 1: External Secrets Operator

ESO synchronises secrets from external providers into standard Kubernetes Secret objects. You define an ExternalSecret resource, and the operator creates or updates a corresponding Secret in your namespace. The secret ends up in etcd, but it is fetched from your provider on each reconciliation cycle.

When it shines: You already use AWS Secrets Manager, Azure Key Vault, GCP Secret Manager, or HashiCorp Vault and want a straightforward bridge into Kubernetes. ESO supports over 40 providers including CyberArk, Akeyless, and Pulumi ESC. It is the easiest path if you want a centralised secret store outside Kubernetes without running your own infrastructure.

When it stumbles: Secrets still live in etcd. If your threat model requires that secrets never touch the Kubernetes datastore, ESO is not the right answer. It also means anyone with etcd access (or a ClusterRole that can read secrets across namespaces) can access your synced data.

Installation:

helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets external-secrets/external-secrets \
  -n external-secrets --create-namespace

Minimal example:

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: aws-secrets
spec:
  provider:
    aws:
      service: SecretsManager
      region: eu-west-2
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: app-creds
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: aws-secrets
    kind: SecretStore
  target:
    name: app-secrets
  data:
    - secretKey: DB_PASSWORD
      remoteRef:
        key: production/app/db
        property: password

ESO watches the remote secret and updates the local Kubernetes Secret when the remote value changes. This makes rotation transparent — rotate the secret in your provider, and the pod picks it up on its next mount or after a rollout restart.

Approach 2: Sealed Secrets

Sealed Secrets takes the opposite approach from ESO. Instead of fetching from an external store, you encrypt your secrets client-side into a SealedSecret custom resource that can be safely committed to git. Only the controller running in your cluster can decrypt it.

The workflow is dead simple:

# Create a local Secret manifest
kubectl create secret generic app-creds \
  --from-literal=api_key=sk-abc123 \
  --dry-run=client -o yaml > secret.yaml

# Encrypt it with kubeseal
kubeseal -f secret.yaml -w sealed-secret.yaml

# This file is now safe to commit to git
git add sealed-secret.yaml

The controller is installed via Helm:

helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
helm install sealed-secrets -n kube-system \
  --set-string fullnameOverride=sealed-secrets-controller \
  sealed-secrets/sealed-secrets

When it shines: You practice GitOps. Sealed Secrets is purpose-built for storing encrypted secrets alongside your application manifests. It supports three scopes (strict, namespace-wide, cluster-wide) that control whether a sealed secret can be renamed or moved between namespaces — a thoughtful design for teams that want fine-grained security.

When it stumbles: Sealed Secrets are bound to a specific cluster’s encryption key. You cannot decrypt a SealedSecret offline without the controller’s private key, which makes disaster recovery more involved. You need to back up the sealing key or use the --re-encrypt workflow if you lose the cluster. The controller manages key rotation automatically (every 30 days by default), but old keys are retained so existing sealed secrets remain functional. Currently at 9.1k GitHub stars with over 1,600 commits, it is one of the most battle-tested options in this space.

Approach 3: SOPS (Secrets OPerationS)

SOPS is a file-level encryption tool that encrypts the values inside YAML, JSON, ENV, and INI files while preserving the structure. It supports AWS KMS, GCP KMS, Azure Key Vault, age, and PGP as encryption backends. Originally created at Mozilla in 2015, it became a CNCF sandbox project in 2023 and is now at v3.13.1 (May 2026) with 21.9k GitHub stars.

The killer workflow is decrypting secrets at deploy time in CI/CD:

# .sops.yaml
creation_rules:
  - path_regex: secrets/*.yaml
    age: age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw

Encrypt a file:

sops encrypt --age age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw \
  secrets/prod.yaml > secrets/prod.enc.yaml

In your deployment pipeline:

# Decrypt and apply in one step
sops decrypt secrets/prod.enc.yaml | kubectl apply -f -

Flux supports this natively with its SOPS integration. ArgoCD can achieve the same via a pre-sync hook or a custom plugin.

When it shines: Multi-cluster, multi-team GitOps. SOPS files are portable — you can decrypt them on any machine with the right key. This makes it the natural choice when you have multiple clusters, or when developers need to decrypt secrets locally without cluster access. The age backend eliminates the complexity of PGP key management while providing strong X25519 encryption.

When it stumbles: SOPS does not handle dynamic secrets. It encrypts values at rest in your repository. If a secret needs to rotate hourly, SOPS is not the tool for that. It also requires your CI/CD pipeline to have the decryption key available, which is a separate security concern you need to manage.

Approach 4: Vault Secrets Store CSI Driver

If your threat model requires that secrets never touch the Kubernetes datastore, the Vault Secrets Store CSI Driver is the right answer. Secrets are mounted into pods as ephemeral CSI volumes — they exist only in the pod’s memory and the Vault server. Nothing is written to etcd.

The architecture involves a SecretProviderClass that defines which Vault secrets to retrieve:

apiVersion: secrets-store.csi.x-k8s.io/v1alpha1
kind: SecretProviderClass
metadata:
  name: vault-db-creds
spec:
  provider: vault
  parameters:
    roleName: app
    objects: |
      - objectName: "db_password"
        secretPath: "database/creds/db-app"
        secretKey: "password"

And a pod that mounts it:

volumes:
  - name: secrets
    csi:
      driver: secrets-store.csi.k8s.io
      readOnly: true
      volumeAttributes:
        secretProviderClass: vault-db-creds

When it shines: Zero-trust environments, regulatory compliance, and dynamic secrets. Vault supports short-lived credentials with automatic lease renewal. The CSI driver authenticates using the pod’s service account (via Vault’s Kubernetes auth method), which means no static tokens or long-lived credentials anywhere in the system.

When it stumbles: You need to run Vault. That is a significant operational investment — Vault Enterprise uses custom pricing (contact IBM HashiCorp sales), and self-managing Vault Community requires dedicated expertise for HA configurations, seal/unseal workflows, and DR. If you are not already running Vault, the operational overhead rarely justifies the security gain over ESO or Sealed Secrets.

Vault also offers the Agent Injector as an alternative that injects secrets via a sidecar container. For new deployments in 2026, the CSI path is the recommended approach — it avoids the sidecar overhead and integrates more cleanly with the Kubernetes volume lifecycle.

Approach 5: Native Kubernetes Secrets (With Encryption-at-Rest)

Before adopting any of these tools, you should at minimum enable encryption-at-rest for etcd. This is not a replacement for the tools above, but a baseline you should have regardless of which approach you choose:

# kube-apiserver encryption config
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources: ["secrets"]
    providers:
      - kms:
          name: myKMS
          endpoint: unix:///var/run/kmsplugin/socket.sock
      - aescbc:
          keys:
            - name: key1
              secret: <base64-encoded-key>
      - identity: {}

This ensures that secrets stored in etcd are encrypted at rest. It does not change how secrets are transmitted or who can read them via the API server — RBAC is still your primary access control layer.

Decision Framework

Teams can successfully use all of these approaches. The choice comes down to three factors:

ScenarioRecommendationRationale
GitOps with Flux/ArgoCD, single clusterSealed SecretsEncrypted in git, cluster-bound, minimal moving parts
GitOps with multi-cluster or multi-teamSOPS + agePortable encrypted files, works with any GitOps tool
Already using cloud secrets managerExternal Secrets OperatorDirect sync, zero new infra, 40+ providers
Need dynamic/short-lived secretsVault CSI DriverMount without etcd, automatic lease renewal
Small team, want simplicityExternal Secrets OperatorOne Helm install, familiar cloud provider integration
Compliance requires no secrets in etcdVault CSI DriverEphemeral volumes, nothing persists in the datastore

Quick Comparison

External Secrets Operator syncs secrets from external providers into Kubernetes Secrets stored in etcd. It is the easiest to set up if you already use cloud secret managers. It does not solve the “secrets in etcd” problem, but it eliminates the “secrets in git” problem entirely.

Sealed Secrets encrypts secrets for a specific cluster using asymmetric cryptography managed entirely within Kubernetes. You never need an external provider. The sealed secrets are safe in public repositories. The trade-off is cluster lock-in — you cannot decrypt a sealed secret without that cluster’s controller.

SOPS encrypts files with cloud KMS or age keys. The encrypted files are portable across any cluster, any CI/CD system, any machine with the right decryption key. It is the most flexible option but requires you to manage key distribution for your pipelines.

Vault CSI Driver is the only option that keeps secrets out of etcd entirely. It requires running Vault, which is a significant operational commitment. For organisations that already run Vault or have strict compliance requirements, it is the gold standard.

Implementation Quick Start

I am going to give you a working path for each tool. These are not exhaustive tutorials — they get you from zero to a running example.

External Secrets Operator:

helm install external-secrets external-secrets/external-secrets \
  -n external-secrets --create-namespace

# Create a SecretStore pointing at your provider
# Create ExternalSecret resources referencing it

Sealed Secrets:

helm install sealed-secrets -n kube-system \
  sealed-secrets/sealed-secrets

# Install kubeseal client
brew install kubeseal  # macOS
# Or download from GitHub releases for Linux

# Encrypt a secret
kubeseal --fetch-cert > mycert.pem
kubeseal --cert mycert.pem -f secret.yaml -w sealed.yaml
kubectl apply -f sealed.yaml

SOPS with age:

# Generate an age key
age-keygen -o age.key

# Encrypt
sops encrypt --age $(cat age.key | age-keygen -y 2>/dev/null) \
  -i secrets.yaml

# Decrypt
sops decrypt secrets.yaml | kubectl apply -f -

Vault CSI Driver:

Install the Secrets Store CSI Driver and Vault provider:

helm repo add secrets-store-csi-driver \
  https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts
helm install csi-secrets-store secrets-store-csi-driver/csi-secrets-store-driver \
  -n kube-system

# Install Vault provider via Vault Helm chart
helm repo add hashicorp https://helm.releases.hashicorp.com
helm install vault hashicorp/vault -n vault --create-namespace \
  --set injector.enabled=false \
  --set csi.enabled=true

Then configure a SecretProviderClass and mount it in your pod as shown in Approach 4 above.

Validation

Whichever approach you choose, verify that:

  1. No secrets exist in plaintext in your git repositories. Run grep -r "password:" git/ --include "*.yaml" to confirm.
  2. RBAC restricts secret access. kubectl auth can-i list secrets --as=system:serviceaccount:default:my-app should return no for workloads that do not need it.
  3. Encryption-at-rest is enabled for etcd. Check /etc/kubernetes/encryption-config.yaml on your control plane nodes.
  4. Audit logging is capturing secret access events. In ESO and Vault, this happens at the provider level. In Sealed Secrets and SOPS, access is controlled by git history and KMS audit logs.

Next Steps

Secrets management is not a one-time decision. As your cluster count grows, your compliance requirements tighten, or your team matures, you will likely migrate between these approaches. I have seen teams start with Sealed Secrets, add ESO when they adopted a cloud secrets manager, and eventually introduce Vault CSI for their most sensitive workloads.

If you already use cloud secret stores, my previous article on Vault vs AWS Secrets vs Azure Key Vault covers the provider comparison in depth. For a broader look at securing Kubernetes workloads, the Kubernetes Pod Security Standards guide is a good follow-up. And if you are running GitOps with Flux or ArgoCD, the Helm vs Kustomize vs ArgoCD comparison covers how secrets fit into the broader deployment picture.