Skip to content

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.
deploy / git surfacehuman surfacemachine surface (parked)cut candidatesSOPS + ageencrypted files in gitsecretctl + agent guardleak guard over every store aboveVaultwardenbrowser vaultrbw (Linux) / bw (macOS)single-item CLI readssame vault,two clientsOpenBaoKV v2 + TTL tokensDESIGN-ONLYonce deployedInfisicalrejectedHashiCorp Vaultcut on licence

Text fallback:

  1. SOPS+age holds the deploy/git surface: files encrypted in the repository they configure.
  2. Vaultwarden is the human vault; rbw on Linux (bw on macOS) reads single items from it on the command line.
  3. OpenBao is the named future home for machine-facing secrets, but nothing runs it yet.
  4. Infisical and HashiCorp Vault were both considered and cut.
  5. secretctl and 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.
You haveUse
A config value a compose stack or a Kubernetes manifest reads at deploy timeSOPS+age - commit the ciphertext next to the file it configures
A password, card, or identity a human looks up in a browserVaultwarden
A machine credential you need on the command line without a browser, on Linuxrbw get ITEM against the same vault
A service secret that needs rotation, a TTL token, or programmatic reads at real scaleNothing today - this is the flip condition for OpenBao; see below
A team that needs a shared UI for looking up service secrets by handReconsider Infisical only if that becomes true - not before

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.

CandidateVerdictWhy
SOPS+ageKept - deploy/git surfaceEncrypted static files in git; nothing has to be running to read one back
Vaultwarden + bw / rbwKept - human surfaceBrowser-integrated password vault; nothing else replaces that role
OpenBaoShortlisted, DESIGN-ONLYNamed future backend for machine-facing secrets; not deployed
InfisicalCutIts value is programmatic bulk reads, which the constraint above does not need
HashiCorp VaultCutBusiness Source License 1.1, licensor IBM Corp1; unseal-key custody does not match a one-human-master-key story
Bitwarden Secrets ManagerDead endCannot run on Vaultwarden - see the maintainer statement below
SaaS secrets managersCutSelf-hosted bias for this fleet, out of scope for the comparison
Local password stores (KeePassXC, pass, gopass)CutDuplicate 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

AxisSOPS+ageVaultwarden + rbwOpenBao (design-only)Infisical
Runtime dependency for a readNone - a file and an age keyThe rbw client, localOne Go binary3 plus integrated Raft storage, no external database4Postgres and Redis alongside its own backend5
Agent/automation access modelFile-level; whole-file decrypt, no partial readrbw get ITEM - one item at a timebao kv get -field=<name> - one named field at a time6CLI/CI env injection is the product’s whole point - a bulk-read shape by design
Git-native encryptionYes - the point of the tool7NoNoNo
Rotation / TTL tokensManual re-encryptNoneYes - every secret carries a lease, auto-revoked at expiry8Yes
AuditGit history of ciphertext changes; no read-audit of a decryptNoneYesYes
LicenceSOPS: MPL-2.07; age: BSD-3-Clause9AGPL-3.010MPL-2.0, LF/OpenSSF-governed fork of Vault3MIT core, separate licence under its ee/ (Enterprise) directories11
Operational costLowest - no server at read timeA database plus its own backup/replication storySingle binary, one raft snapshot file to restore fromA multi-process web stack (app, Postgres, Redis) that every read depends on
Fits a compose .envYes - this is the primary use caseNo - not a file formatNo - not deployedNo
Fits interactive human useNo - not a lookup UIYes - full browser integrationNoHas 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_URL defaults 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

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.

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.

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.

Ranked by what survives losing the infrastructure underneath it, most to least resilient:

  1. SOPS+age - offline encrypted files in git. Server loss is irrelevant; the files and the age key are the whole system.
  2. 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.
  3. Vaultwarden - a database that needs its own backup and replication story, covered separately in Multi-site Vaultwarden.
  4. 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.
what kind of secret is it?a config value read at deploy time(.env, a k8s secret)a password, card, or identitya machine credential an agent or CIreads routinely, wants rotation/TTLSOPS + ageVaultwarden(rbw on Linux, bw on macOS)is the flip condition met?(frequent multi-service reads,or real rotation/TTL need)deploy OpenBaoyesstay on Vaultwarden + rbw,single-item readsno

Text fallback:

  1. A config value read at deploy time (a compose .env, a Kubernetes secret) goes to SOPS+age.
  2. A password, card, or identity goes to Vaultwarden.
  3. 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)?
  4. If yes, that is the case for deploying OpenBao.
  5. If no, it stays on Vaultwarden, read through rbw’s single-item command, the same as any other machine credential today.
ClaimStatusHow it was checked
rbw replaces bw serve as the Linux CLI client, single-item readsTESTEDInstalled and in daily use on a Linux dev box since 2026-09-23
SOPS+age needs nothing running to read a secret backTESTEDThe existing, daily-used deploy/git surface across this fleet’s compose stacks
Vaultwarden’s DATABASE_URL defaults to SQLite, accepts MySQL/MariaDB or PostgreSQLVendor documentation, not a local testdani-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 docsOpenBao 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 licensingDESIGN-ONLY - never deployed under this constraintVendor self-hosting docs and its own repository LICENSE file, read this run511
HashiCorp Vault’s licence names IBM Corp as licensorVendor fact, not a local deploymenthashicorp/vault LICENSE, read this run1

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.

  1. HashiCorp, “vault/LICENSE,” GitHub. https://github.com/hashicorp/vault/blob/main/LICENSE ↩ ↩2 ↩3

  2. dani-garcia/vaultwarden, “Bitwarden Secrets Manager? (Discussion #3368),” GitHub. https://github.com/dani-garcia/vaultwarden/discussions/3368 ↩

  3. OpenBao, “openbao/openbao,” GitHub. https://github.com/openbao/openbao ↩ ↩2 ↩3

  4. OpenBao, “Integrated Storage (Raft),” OpenBao Docs. https://openbao.org/docs/configuration/storage/raft/ ↩ ↩2 ↩3

  5. Infisical, “Self-Hosting Overview,” Infisical Docs. https://infisical.com/docs/self-hosting/overview ↩ ↩2 ↩3

  6. OpenBao, “kv get,” OpenBao Docs. https://openbao.org/docs/commands/kv/get/ ↩ ↩2 ↩3

  7. Getsops, “sops,” GitHub. https://github.com/getsops/sops ↩ ↩2 ↩3

  8. OpenBao, “What is OpenBao?,” OpenBao Docs. https://openbao.org/docs/what-is-openbao/ ↩ ↩2 ↩3

  9. F. Sottile, “age,” GitHub. https://github.com/FiloSottile/age ↩ ↩2

  10. D. Garcia, “vaultwarden,” GitHub. https://github.com/dani-garcia/vaultwarden ↩ ↩2

  11. Infisical, “infisical/LICENSE,” GitHub. https://github.com/Infisical/infisical/blob/main/LICENSE ↩ ↩2 ↩3

  12. D. Garcia, “vaultwarden/.env.template,” GitHub. https://github.com/dani-garcia/vaultwarden/blob/main/.env.template ↩ ↩2

  13. J. Luehrs, “rbw,” GitHub. https://github.com/doy/rbw ↩