Choosing a secrets backend: SOPS+age, Vaultwarden, OpenBao, and Infisical
A self-hosted fleet ends up with more than one place a secret can live: a compose stack’s .env, a browser password vault, and - eventually - a machine-facing store with rotation and TTL tokens. This page is the decision behind that split: which backend owns which kind of secret, the axes that were scored to get there, and the two candidates that lost. It is for anyone running their own infrastructure who has more than one plausible place to put a credential and wants to see the argument behind the choice.
TL;DR:
- The constraint: services create and set their own secrets, a human holds one master key, and nothing needs routine programmatic reads. A backend that optimises for machine-read access at scale is a downgrade under that constraint.
- SOPS+age keeps the deploy/git surface: encrypted files committed alongside the code they configure, readable with nothing running.
- Vaultwarden, read by rbw on Linux, keeps the human surface: the browser-integrated password vault, plus single-item command-line reads for the odd machine credential that still lives there.
- OpenBao is named as the future home for machine-facing secrets - rotation, TTL tokens, service-generated credentials - but it has never been deployed. It stays parked until a specific condition is met (see Why OpenBao stays parked).
- Infisical is cut: its whole value proposition is programmatic, multi-service reads, which the constraint above does not need, and every read would then depend on its own three-part backend being up.
- HashiCorp Vault was cut earlier still, on licence and operational-burden grounds, before OpenBao and Infisical were even compared against each other.
Topology
Section titled “Topology”Text fallback:
- SOPS+age holds the deploy/git surface: files encrypted in the repository they configure.
- Vaultwarden is the human vault; rbw on Linux (bw on macOS) reads single items from it on the command line.
- OpenBao is the named future home for machine-facing secrets, but nothing runs it yet.
- Infisical and HashiCorp Vault were both considered and cut.
secretctland its agent guard sit over every store that is actually in use, whichever it is, and never over a store that only exists as a plan.
Which do I pick
Section titled “Which do I pick”| You have | Use |
|---|---|
| A config value a compose stack or a Kubernetes manifest reads at deploy time | SOPS+age - commit the ciphertext next to the file it configures |
| A password, card, or identity a human looks up in a browser | Vaultwarden |
| A machine credential you need on the command line without a browser, on Linux | rbw get ITEM against the same vault |
| A service secret that needs rotation, a TTL token, or programmatic reads at real scale | Nothing today - this is the flip condition for OpenBao; see below |
| A team that needs a shared UI for looking up service secrets by hand | Reconsider Infisical only if that becomes true - not before |
The constraint
Section titled “The constraint”Every axis below is scored against one stated model: services create and set their own secrets, a human memorises exactly one master key, and nothing needs to read a secret programmatically as a routine operation. Under that model, a backend’s value is inverted from the usual pitch - the feature to want is the absence of a fast, ergonomic bulk-read path, not its presence. secretctl, covered in Keeping credentials out of coding-agent transcripts, exists for the same reason: an LLM coding agent with shell access will read a config file it has a legitimate reason to read, so whatever backend holds the value has to keep that value out of the agent’s context by construction - single-item reads, digest-based redaction, no command that lists everything at once.
Candidates and cuts
Section titled “Candidates and cuts”| Candidate | Verdict | Why |
|---|---|---|
| SOPS+age | Kept - deploy/git surface | Encrypted static files in git; nothing has to be running to read one back |
| Vaultwarden + bw / rbw | Kept - human surface | Browser-integrated password vault; nothing else replaces that role |
| OpenBao | Shortlisted, DESIGN-ONLY | Named future backend for machine-facing secrets; not deployed |
| Infisical | Cut | Its value is programmatic bulk reads, which the constraint above does not need |
| HashiCorp Vault | Cut | Business Source License 1.1, licensor IBM Corp1; unseal-key custody does not match a one-human-master-key story |
| Bitwarden Secrets Manager | Dead end | Cannot run on Vaultwarden - see the maintainer statement below |
| SaaS secrets managers | Cut | Self-hosted bias for this fleet, out of scope for the comparison |
| Local password stores (KeePassXC, pass, gopass) | Cut | Duplicate what Vaultwarden already does, with a worse sync story and no deploy-time write path |
Bitwarden Secrets Manager, Bitwarden’s own machine-secrets product, is not an option on top of Vaultwarden specifically. A Vaultwarden maintainer stated it directly: “I don’t think anything similar to secrets manager will be coming to Vaultwarden,” because Secrets Manager sits under Bitwarden’s commercial licence, not one compatible with Vaultwarden’s AGPL codebase.2
The axes
Section titled “The axes”| Axis | SOPS+age | Vaultwarden + rbw | OpenBao (design-only) | Infisical |
|---|---|---|---|---|
| Runtime dependency for a read | None - a file and an age key | The rbw client, local | One Go binary3 plus integrated Raft storage, no external database4 | Postgres and Redis alongside its own backend5 |
| Agent/automation access model | File-level; whole-file decrypt, no partial read | rbw get ITEM - one item at a time | bao kv get -field=<name> - one named field at a time6 | CLI/CI env injection is the product’s whole point - a bulk-read shape by design |
| Git-native encryption | Yes - the point of the tool7 | No | No | No |
| Rotation / TTL tokens | Manual re-encrypt | None | Yes - every secret carries a lease, auto-revoked at expiry8 | Yes |
| Audit | Git history of ciphertext changes; no read-audit of a decrypt | None | Yes | Yes |
| Licence | SOPS: MPL-2.07; age: BSD-3-Clause9 | AGPL-3.010 | MPL-2.0, LF/OpenSSF-governed fork of Vault3 | MIT core, separate licence under its ee/ (Enterprise) directories11 |
| Operational cost | Lowest - no server at read time | A database plus its own backup/replication story | Single binary, one raft snapshot file to restore from | A multi-process web stack (app, Postgres, Redis) that every read depends on |
Fits a compose .env | Yes - this is the primary use case | No - not a file format | No - not deployed | No |
| Fits interactive human use | No - not a lookup UI | Yes - full browser integration | No | Has a UI, but unused under this constraint |
Licences and architecture, vendor by vendor
Section titled “Licences and architecture, vendor by vendor”Every claim in this section was checked against the vendor’s own repository or docs this run, not carried over from an earlier survey.
- SOPS is MPL-2.0 licensed and describes itself as “a simple and flexible tool for managing secrets.”7 age, the encryption backend SOPS drives here, is BSD-3-Clause licensed: “a simple, modern and secure encryption tool … with small explicit keys, no config options, and UNIX-style composability.”9
- Vaultwarden is AGPL-3.0 licensed, an “unofficial Bitwarden compatible server written in Rust.”10 Its
DATABASE_URLdefaults to a SQLite file and also accepts a MySQL/MariaDB or PostgreSQL connection string - a real database is an option, never a requirement.12 rbw, the Linux CLI client used against that same vault, is MIT licensed.13 - OpenBao is MPL-2.0 licensed, a fork of HashiCorp Vault, governed by the Linux Foundation’s Open Source Security Foundation (OpenSSF) as a Sandbox project under open governance.3 It ships an integrated Raft storage backend that replicates across cluster nodes with no external database,4 stores arbitrary key/value secrets, can generate secrets on demand for a backing system, and attaches a lease to every secret it issues - automatically revoked at expiry.8 Its CLI resolves a single field of a secret directly:
bao kv get -field=<name>.6 - Infisical’s core is MIT licensed; content under its repository’s
ee/directories carries a separate licence - an open-core split, not one licence covering the whole product.11 A self-hosted instance needs PostgreSQL and Redis as backing services, named explicitly in its own Docker Compose and Kubernetes deployment options, and gates SSO and gateway features behind Enterprise licensing.5 - HashiCorp Vault carries a Business Source License 1.1, and its own repository names the licensor as IBM Corp.1
Why SOPS+age keeps the deploy surface
Section titled “Why SOPS+age keeps the deploy surface”Encrypted static files committed to git need nothing running at read time: no server, no daemon, no database to restore before the first secret comes back. That property puts SOPS+age at the top of the durability ladder among every candidate here - server loss is irrelevant to a file that was already encrypted and pushed. The one thing this surface depends on is the age private key itself: whoever holds it can decrypt every file it protects, so the practice that matters is keeping exactly one authoritative on-disk location for that key, not a copy here and a copy there that can drift out of sync. Rotating a credential with secretctl and Using SOPS+age with compose stacks cover the day-to-day mechanics this section only argues for.
Why Vaultwarden and rbw keep the human surface
Section titled “Why Vaultwarden and rbw keep the human surface”A password, a card, and an identity need a browser-integrated vault with autofill; nothing in the machine-facing comparison above replaces that. rbw is the Linux command-line client against that same vault, and it earns its place for one reason: it reads one item at a time (rbw get ITEM), the same leak-safe shape secretctl’s guard needs everywhere else - see secretctl: stores, digests and commands for the rbw: source scheme this feeds. A macOS machine in the same fleet keeps the bw client instead; rbw was a Linux-only decision, not a cross-platform replacement. Running Vaultwarden itself reliably - database migration, cross-site backup, and load-balanced failover - is a separate question from which backend a secret belongs in; Multi-site Vaultwarden covers that once Vaultwarden has already won this role.
Why OpenBao stays parked
Section titled “Why OpenBao stays parked”OpenBao’s migration plan status is, in its own words, “NOT STARTED.” Every phase past the current one - rbw as the Linux client to Vaultwarden - is a written plan, not a running system. The reason to write the plan without executing it: OpenBao only pays for its own operational cost (one more binary, one more raft cluster to keep healthy) once there is an actual need for rotation, TTL tokens, or secrets a service generates for itself through an API. Stated generically, the flip condition is: agent or CI secret reads become frequent and multi-service, or a rotation/dynamic-credential need becomes real. A second, separate condition would reopen the Infisical question instead - if a human-facing lookup UI for service secrets ever needs to serve more than one person.
Until either condition is met, the axes table above is the whole argument for OpenBao: a single Go binary, a raft snapshot as the entire backup, TTL leases, and a CLI shaped for one-field reads, sitting unused rather than half-adopted for secrets that do not need any of that yet.
Why Infisical stays cut
Section titled “Why Infisical stays cut”Infisical is not a poor secrets manager. It is a secrets manager built for the case where secrets need reading programmatically, often, from more than one service - which is exactly the case the stated constraint rules out. Deploying it anyway would mean every credential read depends on Infisical’s own Postgres-plus-Redis-plus-backend stack being up, in exchange for CLI/CI injection and dynamic secrets that go unused. OpenBao wins the same eventual slot with a smaller footprint: one binary and integrated storage, instead of a three-part web stack, for a CLI shape (bao kv get -field=) that matches the single-item constraint directly.
Durability ladder
Section titled “Durability ladder”Ranked by what survives losing the infrastructure underneath it, most to least resilient:
- SOPS+age - offline encrypted files in git. Server loss is irrelevant; the files and the age key are the whole system.
- OpenBao (design-only) - a single binary plus one raft snapshot file is the entire restore path, in principle; nothing has exercised this on the fleet yet.
- Vaultwarden - a database that needs its own backup and replication story, covered separately in Multi-site Vaultwarden.
- Infisical - the data itself would be fine in Postgres, but every read couples availability to a multi-process web stack: the data can be intact and still unreadable while any one of app, Postgres, or Redis is down.
Decision guide
Section titled “Decision guide”Text fallback:
- A config value read at deploy time (a compose
.env, a Kubernetes secret) goes to SOPS+age. - A password, card, or identity goes to Vaultwarden.
- A machine credential an agent or CI would read routinely, wanting rotation or a TTL, raises one question: is the flip condition met (frequent multi-service reads, or a real rotation/dynamic-credential need)?
- If yes, that is the case for deploying OpenBao.
- If no, it stays on Vaultwarden, read through rbw’s single-item command, the same as any other machine credential today.
Evidence: tested vs design-only
Section titled “Evidence: tested vs design-only”| Claim | Status | How it was checked |
|---|---|---|
rbw replaces bw serve as the Linux CLI client, single-item reads | TESTED | Installed and in daily use on a Linux dev box since 2026-09-23 |
| SOPS+age needs nothing running to read a secret back | TESTED | The existing, daily-used deploy/git surface across this fleet’s compose stacks |
Vaultwarden’s DATABASE_URL defaults to SQLite, accepts MySQL/MariaDB or PostgreSQL | Vendor documentation, not a local test | dani-garcia/vaultwarden .env.template, read this run12 |
OpenBao’s KV secrets, leases, integrated Raft storage, and bao kv get -field= | DESIGN-ONLY on this fleet; features confirmed against OpenBao’s own docs | OpenBao has never been deployed here; the feature claims are re-verified vendor documentation, not a local test846 |
| Infisical needs Postgres and Redis, gates SSO/gateways behind Enterprise licensing | DESIGN-ONLY - never deployed under this constraint | Vendor self-hosting docs and its own repository LICENSE file, read this run511 |
| HashiCorp Vault’s licence names IBM Corp as licensor | Vendor fact, not a local deployment | hashicorp/vault LICENSE, read this run1 |
Related docs
Section titled “Related docs”Keeping credentials out of coding-agent transcripts covers the leak-guard layer that sits over whichever backend actually holds a value - this page does not restate its incidents or its registry-scale numbers, which live there so the two pages do not drift against each other. secretctl: stores, digests and commands is the tool those sops:, rbw:, and bw: source schemes belong to. Rotating a credential with secretctl is the task-sequenced use of that tool once a credential lives in one of the backends above. Multi-site Vaultwarden covers running Vaultwarden itself across two sites with automatic failover - a question of keeping the human surface up, not of which surface a secret belongs on.
References
Section titled “References”-
HashiCorp, “vault/LICENSE,” GitHub. https://github.com/hashicorp/vault/blob/main/LICENSE ↩ ↩2 ↩3
-
dani-garcia/vaultwarden, “Bitwarden Secrets Manager? (Discussion #3368),” GitHub. https://github.com/dani-garcia/vaultwarden/discussions/3368 ↩
-
OpenBao, “openbao/openbao,” GitHub. https://github.com/openbao/openbao ↩ ↩2 ↩3
-
OpenBao, “Integrated Storage (Raft),” OpenBao Docs. https://openbao.org/docs/configuration/storage/raft/ ↩ ↩2 ↩3
-
Infisical, “Self-Hosting Overview,” Infisical Docs. https://infisical.com/docs/self-hosting/overview ↩ ↩2 ↩3
-
OpenBao, “kv get,” OpenBao Docs. https://openbao.org/docs/commands/kv/get/ ↩ ↩2 ↩3
-
Getsops, “sops,” GitHub. https://github.com/getsops/sops ↩ ↩2 ↩3
-
OpenBao, “What is OpenBao?,” OpenBao Docs. https://openbao.org/docs/what-is-openbao/ ↩ ↩2 ↩3
-
F. Sottile, “age,” GitHub. https://github.com/FiloSottile/age ↩ ↩2
-
D. Garcia, “vaultwarden,” GitHub. https://github.com/dani-garcia/vaultwarden ↩ ↩2
-
Infisical, “infisical/LICENSE,” GitHub. https://github.com/Infisical/infisical/blob/main/LICENSE ↩ ↩2 ↩3
-
D. Garcia, “vaultwarden/.env.template,” GitHub. https://github.com/dani-garcia/vaultwarden/blob/main/.env.template ↩ ↩2
-
J. Luehrs, “rbw,” GitHub. https://github.com/doy/rbw ↩