Secrets Management Comparison — Vault vs ESO vs SOPS¶
Summary
Canonical comparison of the three most common approaches to secrets for Kubernetes and GitOps: a central secrets platform (HashiCorp Vault), a sync operator (External Secrets Operator), and file encryption in Git (SOPS). They solve different layers and are usually combined rather than chosen exclusively. OpenBao, the MPL-2.0 fork of Vault, is covered as an alternative inside the Vault topic (Vault and OpenBao at a Glance).
Quick Reference¶
| Dimension | HashiCorp Vault | External Secrets Operator | SOPS |
|---|---|---|---|
| Type | Centralized secrets platform | Kubernetes sync operator | File encryption CLI and Go library |
| Latest Version | 2.1.1 (2026-09-16) | 2.11.0 (2026-09-18) | 3.13.3 (2026-07-23) |
| Architecture | Client-server cluster (Integrated Storage / Raft) | Kubernetes controller (three Deployments: core, webhook, cert controller) | CLI (no daemon) |
| Dynamic secrets | Yes (database, cloud, SSH, PKI; auto-revoked leases) | Partly, via generators (VaultDynamicSecret, ECRAuthorizationToken, Password, v1alpha1) |
No |
| Encryption as a service | Yes (Transit engine) | No | No (encrypts files, not a service) |
| PKI / certificates | Yes (root and intermediate CAs, ACME) | No | No |
| License | BSL 1.1 (source-available; Enterprise commercial) | Apache-2.0 | MPL-2.0 |
| Governance | IBM (HashiCorp acquisition closed 2025-02-27) | CNCF Sandbox | CNCF Sandbox; TOC health review open since 2026-03 (cncf/toc#2098) |
| Open-source alternative | OpenBao 2.7.0 (MPL-2.0, OpenSSF) | Vault Secrets Operator (Vault-only), Secrets Store CSI Driver | Sealed Secrets |
| Operational cost | High (HA cluster, unseal, upgrades, audit) | Low (operator; upgrade often, only the newest minor is supported) | Minimal (CLI; key distribution is on you) |
Which One Should I Pick?¶
The decision usually starts from where secrets live today and whether you need credentials generated on demand.
flowchart TD
Q1{"Need dynamic credentials,<br/>PKI or encryption as a service?"}
Q1 -->|"Yes"| Q2{"Strict OSI open-source<br/>license required?"}
Q2 -->|"Yes"| BAO["OpenBao<br/>(MPL-2.0 Vault fork)"]
Q2 -->|"No"| VAULT["HashiCorp Vault<br/>(CE, Enterprise or HCP)"]
Q1 -->|"No"| Q3{"Secrets already in a cloud<br/>secret manager or Vault?"}
Q3 -->|"Yes"| Q4{"Consumers run on<br/>Kubernetes?"}
Q4 -->|"Yes"| ESO["External Secrets Operator<br/>(sync into K8s Secrets)"]
Q4 -->|"No"| SDK["Provider SDK / CLI<br/>or Vault Agent"]
Q3 -->|"No"| Q5{"Want secrets versioned<br/>in Git with no server?"}
Q5 -->|"Yes"| SOPS["SOPS<br/>(age or cloud KMS)"]
Q5 -->|"No"| CLOUD["Adopt a cloud secret manager,<br/>then add ESO"]
VAULT -.->|"deliver to pods"| K8S["ESO, VSO or CSI provider"]
BAO -.->|"deliver to pods"| K8S
| Scenario | Recommendation |
|---|---|
| Simple GitOps secrets, small team | SOPS with age: encrypt in Git, Flux decrypts on-cluster |
| Secrets already in AWS SM, GCP SM, Azure Key Vault | ESO with a ClusterSecretStore per provider |
| Dynamic database or cloud credentials | Vault (or OpenBao): short-lived, auto-revoked leases |
| Internal PKI / certificate authority | Vault PKI engine (or OpenBao) |
| Application-level encryption, signing, HMAC | Vault Transit |
| Strict OSI license requirement for the central store | OpenBao instead of Vault |
| Enterprise, regulated, central audit of every read | Vault (Enterprise or HCP) + ESO or VSO |
| Secrets must never be stored in etcd | Secrets Store CSI Driver or Vault Agent injection (not ESO or SOPS-to-Secret) |
| Layered stack | SOPS (bootstrap secrets in Git) + ESO (sync) + Vault (generate) |
How They Work Together¶
The layered pattern uses each tool for what it does best: SOPS protects the few bootstrap credentials that must live in Git, ESO carries references (not values) through GitOps, and Vault issues the real credentials.
flowchart LR
DEV["Developer"] -->|"sops encrypt<br/>(bootstrap creds)"| GIT["Git repository"]
DEV -->|"ExternalSecret manifests<br/>(references only)"| GIT
GIT --> FLUX["Flux kustomize-controller<br/>(decrypts SOPS)"]
FLUX -->|"apply"| ESO_C["ESO core controller"]
ESO_C -->|"Kubernetes auth,<br/>read KV or lease creds"| VAULT_C["Vault<br/>(KV, database engine)"]
ESO_C -->|"create / update"| SEC["Kubernetes Secret"]
SEC -->|"env or volume"| APP["Application Pod"]
style ESO_C fill:#1565c0,color:#fff
style VAULT_C fill:#000,color:#fff
Complementary, not competitive
These tools sit at different layers. ExternalSecret manifests contain only references, so they can be committed in plaintext; SOPS is needed only for values that must be in Git (for example, the credentials a SecretStore uses when workload identity is unavailable).
Feature Matrix¶
| Feature | Vault | ESO | SOPS |
|---|---|---|---|
| Secret storage | Yes (KV v2, versioned) | No (bridges external stores) | Yes (encrypted files in Git) |
| Dynamic secrets | Yes (database, AWS, GCP, Azure, SSH, Kubernetes) | Partly (generators, v1alpha1) |
No |
| Secret rotation | Automatic for dynamic secrets and database static roles; KV not rotated | Propagates provider changes on refreshInterval (default 1h) |
Manual (updatekeys + rotate) |
| Audit logging | Yes, audit devices log every request | No own audit log; relies on provider logs and Kubernetes audit | Via KMS / Key Vault / Vault-OpenBao Transit logs; optional PostgreSQL decrypt log; none for age or PGP |
| Kubernetes integration | Kubernetes auth, Agent injector, CSI provider, Vault Secrets Operator (VSO), or ESO | Native Kubernetes Secrets via CRDs | Flux native; Argo CD via KSOPS or helm-secrets |
| GitOps compatible | Values not in Git; config via Terraform | Yes (CRDs in Git hold references only) | Yes (encrypted files in Git) |
| Providers / key backends | Is the provider; auto-unseal via cloud KMS or HSM | About 40 providers (ten stable), including Vault, OpenBao, AWS, GCP, Azure |
age (incl. SSH, plugin, post-quantum), PGP, AWS KMS, GCP KMS, Azure Key Vault, Vault/OpenBao Transit, HuaweiCloud KMS |
| Push to provider | Enterprise secrets sync to cloud secret managers | PushSecret (v1alpha1) |
No |
| Complexity | High | Low to medium | Low |
Integration Patterns¶
SOPS + Flux GitOps Pipeline¶
The most common GitOps secrets pattern. SOPS encrypts secret values in YAML (keys remain plaintext for readable diffs), and Flux's kustomize-controller decrypts them on-cluster before applying. Details: SOPS GitOps decryption models.
sequenceDiagram
participant Dev as Developer
participant Git as Git Repository
participant Src as Flux source-controller
participant Kust as Flux kustomize-controller
participant K8s as Kubernetes API
Dev->>Dev: sops encrypt secret.yaml (age recipient)
Dev->>Git: git push (encrypted YAML)
Src->>Git: Fetch revision
Src-->>Kust: Artifact ready
Kust->>Kust: Detect sops metadata, decrypt with age key Secret or KMS identity
Kust->>K8s: Server-side apply plaintext Secret
Key configuration:
- Create
.sops.yamlin the repository root withcreation_rules(path_regex,encrypted_regex: ^(data|stringData)$, recipients). - Store the age private key as a Kubernetes Secret in
flux-system, or use cloud KMS / OpenBao workload identity. - Set
spec.decryption.provider: sopson the FluxKustomization. - Prefer age over PGP (the SOPS docs recommend age; PGP is not deprecated). Move to cloud KMS when you need central revocation and audit.
Never apply encrypted Secrets with kubectl
SOPS-encrypted Secrets are designed for consumption by kustomize-controller. Applying them directly with kubectl apply creates a Secret containing ciphertext, not plaintext.
ESO + Vault with Kubernetes Auth¶
ESO reads static secrets from Vault KV through a SecretStore, and can request dynamic credentials through the VaultDynamicSecret generator. Kubernetes auth is the usual production pattern. Details: ESO how-to guides and the ESO Vault provider.
sequenceDiagram
participant ESO as ESO core controller
participant K8s as Kubernetes API
participant Vault as Vault server
participant App as Application Pod
ESO->>K8s: Request ServiceAccount token (TokenRequest)
ESO->>Vault: POST /v1/auth/kubernetes/login (JWT, role)
Vault->>K8s: TokenReview (validate JWT)
Vault-->>ESO: Vault token with policies
ESO->>Vault: GET /v1/secret/data/myapp (KV v2)
Vault-->>ESO: Secret payload
ESO->>K8s: Create or update Kubernetes Secret
App->>K8s: Consume Secret as env or volume
Key configuration:
- Enable Vault Kubernetes auth:
vault auth enable kubernetes. - Bind the ESO ServiceAccount to a Vault role with a scoped policy.
- Create a
SecretStore(orClusterSecretStorewith namespaceconditions) with the Vault URL and Kubernetes auth mount. - Create
ExternalSecretresources mapping Vault paths to Secret keys, with arefreshInterval(for example,1h). - Alternative: HashiCorp's own Vault Secrets Operator provides similar CRD-to-Secret sync, for Vault only.
Layered Stack: SOPS + ESO + Vault¶
| Layer | Tool | Responsibility |
|---|---|---|
| Git encryption | SOPS | Encrypt the few bootstrap values that must live in Git |
| Kubernetes sync | ESO | Turn ExternalSecret references into native Kubernetes Secrets |
| Secret generation and storage | Vault | Store KV secrets; issue dynamic, short-lived credentials (database, cloud) |
Workflow: a developer commits plaintext ExternalSecret manifests (references only) and, if needed, SOPS-encrypted bootstrap credentials. Flux decrypts and applies them. ESO fetches the actual values from Vault and creates Kubernetes Secrets, which the application consumes normally.
Start simple, add layers as needed
Many teams start with SOPS only. Add ESO when a central store exists or multi-cluster sync is needed. Add Vault (or OpenBao) when you need dynamic credentials, PKI, or central audit. Avoid adopting the full stack on day one.
Operational Overhead¶
| Dimension | Vault | ESO | SOPS |
|---|---|---|---|
| Deployment | Helm chart or packages; Integrated Storage (Raft) recommended, Consul legacy | Helm chart: core controller, webhook and cert controller Deployments + CRDs | Single binary (no deployment) |
| Infrastructure | 3 or 5 Raft voters (5 across 3 zones for production), SSD storage | Runs in the existing cluster; HA needs replicaCount > 1 and leaderElect: true |
None |
| Init / unseal | Shamir unseal or auto-unseal via cloud KMS (HSM in Enterprise) | N/A | N/A |
| Maintenance | Upgrades (CE patches only the newest line), policies, leases, audit devices, snapshots | Upgrade every few weeks (only the newest minor is supported); monitor sync status | Key distribution and rotation; updatekeys / rotate after staff changes |
| Backup | Raft snapshots (test restores) | CRDs in Git; values live in the provider | Keys in secure storage; encrypted files in Git |
| Team skill | High: needs a platform team | Low to medium: Kubernetes operator familiarity | Low: CLI and GitOps familiarity |
| On-call burden | High: quorum, unseal, lease exhaustion, audit device failures | Low: provider outages leave the last synced Secret in place | Near zero: no running process |
| Estimated FTE (estimate, unsourced) | 0.25-0.5 | ~0.05 | ~0 |
Cost Analysis¶
Licensing¶
| Tool | License | OSI open source? | Notes |
|---|---|---|---|
| Vault Community | BSL 1.1 (1.15.0 and later; each release converts to MPL 2.0 after four years) | No (source-available) | Free to use; BSL restricts offering competing hosted products |
| Vault Enterprise | Commercial | No | Namespaces, DR and performance replication, HSM, Sentinel, secrets sync; IBM Support Cycle-2 lifecycle since 2.0 |
| OpenBao | MPL 2.0 | Yes | Linux Foundation / OpenSSF; namespaces included, no DR/performance replication |
| ESO | Apache-2.0 | Yes | CNCF Sandbox; Red Hat ships a supported OpenShift build |
| SOPS | MPL-2.0 | Yes | CNCF Sandbox; licensing is the subject of the open TOC health review |
Infrastructure Costs¶
Rough estimates
The self-hosted figures below are order-of-magnitude estimates (instance counts follow HashiCorp's 3 or 5 voter guidance and hardware sizing), not quotes. HCP Vault Dedicated is billed as an hourly cluster base cost plus, on Essentials and Standard, a per-client charge; check the official pricing page for current numbers.
| Scenario | Vault (self-hosted, estimate) | Vault (HCP Dedicated) | ESO | SOPS |
|---|---|---|---|---|
| Small (< 10 services) | 3 small VMs (~$100/mo) | Development tier (single node, 25-client limit, no SLA) or Essentials | Free (runs in existing cluster) | Free |
| Medium (50 services) | 3 mid-size VMs (~$300/mo) | Essentials: cluster base + per-client fees | Free | Free |
| Large (200+ services) | 5 larger VMs (~$700/mo) | Standard or contract pricing | Free | Free |
HCP Vault per-client fees
Infisical's Vault pricing guide (2026 edition, checked 2026-09-28) lists $72.92 per client per month on Essentials and Standard, on top of about $1,152 per month for an Essentials cluster. HashiCorp's tiers and features page confirms that Essentials adds a flat hourly cost per Vault client, counted once per monthly billing period. A client is any unique entity that authenticates (application, service, or user), so per-client fees can exceed the cluster cost at modest scale. Self-hosted Community Edition has no per-client fee but needs operational expertise.
Total Cost by Team Size¶
These are illustrative estimates (unsourced) to show relative scale, not budgets.
| Team size | Typical stack | Estimated annual cost |
|---|---|---|
| 1-5 engineers | SOPS only | ~$0 (key management time) |
| 5-20 engineers | SOPS + ESO (+ cloud secret manager) | Cloud secret manager fees + team time |
| 20-50 engineers | ESO + Vault CE or OpenBao (self-hosted) | ~$5,000-15,000/yr infrastructure (estimate) |
| 50+ engineers, regulated | SOPS + ESO + Vault Enterprise or HCP | License or HCP fees dominate. Not published for self-managed Vault Enterprise ("Contact sales" on hashicorp.com/pricing); HCP Vault Dedicated bills per cluster-hour plus, on Essentials and Standard, a per-client charge (HCP pricing definitions, checked 2026-09-28) |
Multi-cluster Secret Distribution¶
| Capability | Vault | ESO | SOPS |
|---|---|---|---|
| Architecture | One Vault cluster serves many Kubernetes clusters | ESO per cluster, all pointing to the same provider | Encrypted files in a shared Git repository |
| Cross-cluster access | Each cluster authenticates via its own Kubernetes auth mount | Per-cluster ESO + shared backend, or the Kubernetes provider (beta) reading a remote cluster |
Each cluster decrypts independently |
| Push model | Pull model; Enterprise secrets sync pushes to cloud secret managers | PushSecret (v1alpha1) writes to a provider, including a remote cluster via the Kubernetes provider |
N/A |
| Consistency | Single source of truth | Eventual (per refreshInterval, default 1h) |
Git push + GitOps reconcile |
| Secret scoping | Policies per auth role; namespaces (Enterprise or OpenBao) | Namespaced SecretStore, ClusterSecretStore conditions |
.sops.yaml creation rules per path |
| Failure mode | New reads fail if Vault is unreachable; existing leases run to TTL | Last synced Secret stays; no updates until the provider recovers | Secrets persist; no updates until Git sync |
Multi-cluster Patterns¶
Centralized Vault (hub and spoke):
- One Vault cluster with a Kubernetes auth mount per target cluster.
- Each cluster's ESO (or VSO) authenticates with its own ServiceAccount.
- Vault policies scope access per cluster and namespace.
- Best for: regulated environments that need centralized audit.
Distributed ESO with a shared backend:
- ESO in every cluster, each with a
ClusterSecretStorepointing to the same provider (AWS SM, GCP SM, Azure Key Vault). - Secrets are defined once in the provider and synced everywhere.
- Best for: cloud-native teams using managed secret stores.
Git-centric (SOPS + Flux):
- Encrypted secrets in a shared Git repository with per-cluster overlays.
- Each cluster runs Flux and decrypts with its own age key or KMS identity.
- Best for: teams that want everything in Git and no external dependency.
Compliance Considerations¶
This table summarizes capabilities relevant to common frameworks; it is not a compliance attestation.
| Framework | Vault | ESO | SOPS |
|---|---|---|---|
| SOC 2 | Strong: audit device logs every request, ACL policies, encryption at rest and in transit, leases | Partial: inherits audit from the backend; add Kubernetes audit logging | Partial: Git history plus KMS logs; no runtime audit with age or PGP |
| HIPAA | Strong: Transit, dynamic credentials, audit logs | Depends on the backend (Vault, AWS SM, etc.) | Weak: no automatic rotation; access audit only with a KMS backend |
| PCI-DSS | Strong: HSM (Enterprise), access controls; tokenization via the Transform engine (Enterprise ADP) | Partial: no own key management; depends on the backend | Weak: no key ceremony support, no runtime access controls |
| FedRAMP | No FedRAMP authorization announced for HCP Vault Dedicated; FedRAMP was a roadmap item in 2024 (TechTarget), with nothing newer found (checked 2026-09-28). Self-managed Vault Enterprise offers FIPS builds | N/A (not a service) | N/A |
| GDPR | Data residency follows where clusters run; namespaces (Enterprise) separate tenants | Depends on the backend's data residency | Region-scoped cloud KMS keys |
Compliance does not mean just one tool
No single tool satisfies a framework on its own. Vault provides the strongest audit and key management story, but the overall posture also depends on Kubernetes RBAC, etcd encryption at rest, network policies, cloud controls, and process. ESO and SOPS complement a central store; they do not replace its audit trail.
Related¶
- Topics: Vault, External Secrets Operator, SOPS
- Domain: Secrets overview, Comparisons index, Identity Provider Comparison
- GitOps: Flux, Argo CD
- Platform: Kubernetes
Sources¶
- Vault documentation and release notes
- Vault Secrets Operator
- HCP Vault Dedicated tiers and features
- HashiCorp Vault pricing
- Vault Pricing Analysis (Infisical, third party)
- OpenBao
- External Secrets Operator documentation and stability and support
- ESO HashiCorp Vault provider and Kubernetes provider (cross-cluster)
- SOPS documentation and GitHub repository
- CNCF TOC health review: SOPS (#2098)
- Flux: Manage Kubernetes secrets with SOPS and Flux secrets management overview
- Tailscale: Sync Kubernetes Secrets across clusters with ESO