In March 2026, the CNCF's Technical Oversight Committee opened a formal health review on SOPS — the tool that roughly every GitOps shop uses to keep encrypted secrets in the same git repo as everything else. The question on the table wasn't a feature gap. It was whether SOPS can stay in CNCF at all. Its license, Mozilla Public License 2.0, has never been approved as a primary license for a CNCF project, and the TOC's options are blunt: relicense, or get archived and pushed out to some other foundation.
That review is still open as of this writing. In the same stretch of 2026, SOPS shipped three point releases — v3.13.0, v3.13.1, v3.13.2 — and its GitHub star count climbed past 22,400. A project a standards body is actively debating whether to keep is, at the same time, more embedded in more GitOps pipelines than it has ever been. That's the tension worth unpacking, because the answer to "should a self-hosted platform default its secrets story to SOPS" doesn't hinge on the license drama — it hinges on what SOPS plus age encryption mechanically buys you, and where it mechanically runs out. Here's the concrete version of both, plus the specific thresholds that should push a fleet toward Vault or External Secrets Operator (ESO) instead.
What SOPS + age actually does
SOPS (Secrets OPerationS) encrypts the values in a YAML or JSON file and leaves the keys readable. A secret checked into git looks like this:
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
stringData:
password: ENC[AES256_GCM,data:Tqz8...,iv:xY2...,tag:mAc...,type:str]
sops:
age:
- recipient: age1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpq...
lastmodified: "2026-07-12T09:14:00Z"You can git diff this file and see exactly which key changed, even though you can't read the new value — that's a deliberate design choice, and it's most of the reason SOPS wins over encrypting whole files. age, the encryption backend SOPS now defaults its recipient config to, replaced GPG for most new setups: X25519 keys are two lines to generate and paste, versus a GPG keyring most CI runners have to be taught to trust.
The delivery mechanics differ slightly by GitOps tool, and neither goes through a running secrets service:
- Flux decrypts natively. A
Kustomizationresource references asops-ageSecret holding the private key, sitting only in theflux-systemnamespace, and Flux decrypts during reconciliation before applying. - ArgoCD has no native SOPS support, so the common path is KSOPS, a Kustomize exec plugin baked into a custom
argocd-repo-serverimage. Kustomize invokes KSOPS, KSOPS decrypts, plaintext flows into the manifest ArgoCD applies.
Either way, the private key exists in exactly one place — the cluster's reconciler — and the encrypted file is safe to commit, mirror, back up, and hand to a contractor's read-only clone of the repo.
The real argument: nothing else to run
Compare what each option actually asks a small self-hosted fleet to operate, not just what it protects:
| Running components | Storage backend | Failure mode if it's down | |
|---|---|---|---|
| SOPS + age | None. Decryption is a local key operation inside the existing GitOps reconciler. | The git repo itself. | Reconciliation stalls with everything else if git or the cluster is down — no separate outage to have. |
| HashiCorp Vault | Vault server(s), typically 3+ nodes for HA, an unseal process (or auto-unseal via a cloud KMS), a storage backend (Raft, Consul, or a cloud blob store) | Vault's own storage layer, backed up and monitored independently | Vault down means every pod requesting a dynamic secret at startup blocks, even if the cluster and git are fine |
| External Secrets Operator | The ESO controller itself, plus whatever backend it's pointed at — Vault, AWS Secrets Manager, GCP Secret Manager | Whichever cloud secrets manager you picked | ESO adds a sync-interval controller on top of Vault's own failure modes, or ties you to a cloud API a self-hosted platform may not otherwise depend on |
That last row is the detail the TODO spec for this piece flagged and it holds up: ESO isn't actually an alternative to Vault, it's a client for Vault or a cloud secrets manager. Picking ESO as your "we don't want to run Vault" answer usually means you end up running Vault anyway, just behind an extra controller. For a platform whose whole pitch is "machines you own, no managed dependency you don't control," reaching for a cloud secrets manager as the backend defeats the point.
SOPS + age has zero rows in the "running components" column. That's not a minor convenience for a Cluster-API-based platform standing up its first few tenant clusters — it's one fewer stateful service with its own upgrade cadence, backup schedule, and unseal ritual, on a team that's already operating etcd, the CNI, and the CAPI controllers themselves.
Where it stops being enough — with the actual thresholds
None of the above means SOPS scales indefinitely. Three specific limits, each with a concrete trigger point, are why a growing platform outgrows the SOPS-only default:
1. Rotation cost scales per file, linearly, with no shortcut. Rotating an age key isn't a single command — sops updatekeys (or sops --rotate for the data key) has to run against every encrypted file that key can decrypt, then get committed and reconciled. Scripted, that's a five-minute job across a few dozen files — one file per environment, say. On a multi-tenant platform where secrets are scoped per tenant per environment, the file count climbs into the hundreds, and a "rotate the compromised key" incident stops being a script run and becomes a project with its own runbook. Vault's dynamic secrets sidestep this entirely: a credential that already expires in 15 minutes doesn't need an emergency rotation, because it was never going to outlive the incident window anyway.
2. There's no per-secret audit trail, and there structurally can't be. Decryption with age is local key math — no server call, no log line, nothing to query. Flux's reconciliation events tell you that a Kustomization synced, not which secret got read or by whom. The first time an auditor asks "who decrypted the production database password on June 3rd, and prove it" for a SOC 2 CC6.1 control or a PCI-DSS 10.2 access-logging requirement, SOPS has no answer to hand over. Vault's audit log, or a cloud KMS's decrypt-call logging behind ESO, does. This is usually the trigger that actually forces the migration — not a scaling limit, but a compliance conversation the platform didn't previously need to have.
3. Access is all-or-nothing per key. Anyone (or any service) holding an age private key can decrypt every file encrypted to that recipient, full stop — there's no SOPS-native way to say "this key can read staging secrets but not production." Teams work around it with separate key pairs per environment (already common practice, and worth doing from day one), but that's a manual convention enforced by discipline, not a policy engine. The moment more than one team needs genuinely different, enforced access scopes to different secret subsets — not just different keys they're trusted to keep separate — that's Vault's ACL system doing a job SOPS was never built to do.
None of these three costs anything at small scale — a handful of clusters, one ops team, secrets that rotate on a calendar rather than a timer. They compound specifically as tenant count, team count, or credential-lifetime requirements grow, which is exactly the "small fleet" boundary the TODO spec for this piece was pointing at.
The CNCF license question is a real risk, but a low-stakes one
The open TOC health review matters for anyone deciding whether to build on SOPS today, so it's worth being direct about what's actually at stake instead of hand-waving it. If the maintainers can't or won't relicense away from MPL-2.0, the CNCF's own options are archiving the sandbox listing or the project moving to a different foundation. Either outcome is a governance change, not a product death — SOPS is a stable, widely-forked CLI with no server component and no hosted dependency; nothing about a CNCF sandbox listing lapsing breaks an existing pipeline that already has the binary pinned and the encrypted files already committed.
That's the real asymmetry against Vault or a cloud-backed ESO setup: if SOPS the project stalled entirely tomorrow, a self-hosted fleet still owns every encrypted file, in its own git repo, decryptable by keys it already holds. There's no subscription to lose, no hosted control plane that stops answering, no migration deadline imposed by someone else's business decision — which is the same reason a Cluster-API-based platform doesn't want its default secrets story depending on a vendor's roadmap in the first place. Track the TOC issue as due diligence. It's not a reason to skip SOPS as the default today.
The decision rule
Default to SOPS + age for a self-hosted platform's own secrets story until one of these three trips, in which case reach for Vault (and ESO only as Vault's Kubernetes-facing sync layer, not as a Vault substitute):
- An audit trail requirement shows up — a customer's compliance questionnaire, a SOC 2 or PCI-DSS control — that asks who decrypted what, and when.
- A workload needs credentials shorter-lived than your team can realistically re-encrypt and reconcile by hand — dynamic database credentials for an AI agent's task run, for instance, where the TTL is measured in minutes rather than the calendar-driven cadence a
sops rotatescript can keep up with. - More than one team needs enforced, different access to different secret subsets, not just separate keys they're trusted to keep apart.
Until then, the license-embattled, still-shipping tool with nothing to run is doing exactly the job it's built for.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Its own tenant-manifest secrets default to SOPS + age for exactly the reasons above: no extra stateful service to operate, and nothing to lose if a project's governance status changes. Star the repo on GitHub or deploy your first app today.



