Secrets

4 min read

Teploy has two secret providers. Start with the local one — it needs nothing extra to run. Move to OpenBao when you need scoped per-app credentials, database passwords that rotate themselves, or an audit trail of who accessed what.

Local (default)

teploy secret set API_KEY=secret123
teploy secret get API_KEY
teploy secret list         # keys only, values masked
teploy secret rotate API_KEY

Values are encrypted at rest with age (X25519), decrypted by the CLI on demand, and merged into the same env file your app reads — the difference from a plain teploy env set value is at-rest encryption and audit-friendly access, not a different delivery mechanism. No server-side process to run.

Reference a local secret from teploy.yml:

env:
  API_KEY: secret:API_KEY

OpenBao

OpenBao is the open-source Vault-compatible secrets manager Teploy integrates as a first-class accessory. Everything lives under teploy secret --provider openbao (or the OpenBao-only subcommands below, which don't need the flag).

Setup

teploy secret setup --provider openbao

Idempotent: deploys OpenBao on the private teploy network, initializes it, and auto-unseals with a generated seal key stored in the local age secret store — never plaintext on disk. teploy secret status reports seal/init state. --restart is always reboot-safe: the container comes back initialized and unsealed with zero intervention.

Off-box key isolation, for teams that don't want the unseal key anywhere near the app server:

teploy secret setup --provider openbao --seal awskms \
  --kms-key-id <id> --kms-region us-east-1

teploy secret setup --provider openbao --seal transit \
  --transit-address https://unsealer.internal:8200 --transit-token <token>

Seal credentials ride a 0600 host env file, never the world-readable OpenBao config.

High availability — a multi-node Raft quorum instead of a single file-storage node:

teploy secret setup --provider openbao --replicas 3

Each node auto-unseals with the shared seal and joins the leader via retry_join. The primary keeps the base container name (so every downstream command targets it unchanged); followers get -N suffixes. Killing a node still under quorum keeps serving reads and writes; the killed node auto-unseals and rejoins on restart.

Put, get, list

teploy secret put --provider openbao db password=s3cr3t host=pg.internal
teploy secret get --provider openbao db
teploy secret list --provider openbao

put accepts multiple key=value fields in one secret (db#password, db#host, ...). These commands use the OpenBao root token — an admin path, distinct from what your app is scoped to access (see AppRole below).

Deploy-time injection

env:
  DB_PASSWORD: "secret:db#password"   # -> secret/<app>/db, key "password"
  API_KEY: "secret:apikey"             # #field defaults to "value" for a flat secret

Every app gets its own least-privilege AppRole and policy on teploy secret setup: read-only on secret/<app>/*, nothing else, no write access even to its own secrets. secret:name#field references are resolved once per deploy and merged into the same secret-safe env file local secrets use — never argv, never plaintext on disk.

Dynamic database credentials

Short-lived, auto-revoked credentials instead of a static password:

teploy secret db setup --provider openbao       # against a Postgres accessory
teploy secret db creds --provider openbao        # fetch on demand

For zero app-side plumbing, run an Agent sidecar that auths as the app's own AppRole, templates the credentials to a shared volume, and auto-renews the lease before it expires:

secret:
  provider: openbao
  agent: true

The app reads DB_USER/DB_PASS from /vault/secrets/db.env, mounted read-only. The credentials connect to Postgres as a per-lease, revocable user — not the root account.

Static-role rotation

For a fixed app account whose password should still rotate automatically, rather than minting a new user per request:

teploy secret db static-role --username appuser --rotation-period 24h --admin-pass <admin-pw>
teploy secret db static-creds --username appuser

OpenBao takes over the existing appuser row and rotates its password on the given period — the account identity stays the same, only the credential changes.

Audit streaming

teploy secret audit ship --provider openbao     # one-shot
teploy secret audit enable --provider openbao    # continuous, via a systemd timer
teploy secret audit disable --provider openbao

Every secret access — read, write, dynamic credential issue — streams into teploy-observe's tamper-evident, hash-chained audit trail (see audit: in the config reference). enable installs a timer that runs ship --local on an interval; it's idempotent, so re-running never double-ships an event.

Coming from teploy vault

Earlier releases used a standalone teploy vault command. It's been folded into teploy secret --provider openbao for consistency with the other provider-abstracted commands (network --provider, etc.) — same capabilities, vault:name#key references become secret:name#field, and the vault: config block becomes secret: { provider, accessory, agent }.