Skip to content
VaultTerm
Browse docs

self-hosting

Backups and recovery

Back up the database, .env, and data volume; protect the one irreplaceable master key; and optionally bind it to the license or escrow it with Shamir-split recovery.

Updated Jun 23, 2026

A self-hosted VaultTerm has three things worth backing up and one thing you cannot regenerate. This page covers the routine backup, the master key, and the optional cryptographic protections that make a cloned, forged, or re-hosted instance unable to decrypt.

What to back up

WhatHow
Databasepg_dump piped through gzip (below)
.envcopy it out-of-band — it holds DEV_MASTER_KEY
db-data volumethe Postgres data volume backing the db container

Back up the database with a logical dump:

docker compose exec -T db pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB" | gzip > vaultterm-$(date +%F).sql.gz

Restore it into a fresh stack:

gunzip -c vaultterm-YYYY-MM-DD.sql.gz | docker compose exec -T db psql -U "$POSTGRES_USER" "$POSTGRES_DB"

Migrations are forward-only, so always back up before an upgrade — the backup is also your rollback path. See Upgrading.

The master key is irreplaceable

With the default local key provider, DEV_MASTER_KEY wraps every stored credential. It is the one thing you cannot regenerate: lose it and vault contents are unrecoverable. It lives in .env, which is why .env is on the backup list — guard that backup as carefully as the live secret. See Cryptography for the envelope-encryption model.

Optional: bind the master key to the license

You can make the license cryptographically load-bearing for decryption. With LICENSE_BIND_MASTER=1 and a BOUND_MASTER_CIPHERTEXT, the master key is sealed under an unlock derived from the root seed, the license signature, and the host fingerprint — so a copied, forged, or re-hosted instance boots but cannot open the vault. There is no removable license check to bypass; the wrong host or license simply yields the wrong unlock and every data key stays wrapped.

Provision it on the target host, after the .vtlic is in place:

cd server && npm run bind-master

This mints the master and prints the ciphertext to set as BOUND_MASTER_CIPHERTEXT; then unset DEV_MASTER_KEY. Under production, a failed unlock at boot is fatal, so an instance that cannot decrypt never serves traffic.

Optional: recovery escrow

Binding the master to its host means a dead host, a lost license, or a rebuild would otherwise orphan every data key. The recovery escrow seals the master a second, host-independent way so the legitimate owner can recover but a thief cannot. It seals the master under a random recovery key and 2-of-3 Shamir-splits that key into three shares.

cd server && npm run provision-recovery -- --mode customer-only   # or --mode managed

The mode decides who holds the shares:

ModeWho holds what
customer-only (default)all three shares go to the customer; the vendor holds nothing and mathematically cannot read or recover the vault. Losing the shares is terminal.
managedthe vendor holds one break-glass share — never enough alone — so a customer who loses a share is still recoverable from any two of three.

Recover onto new hardware with two of the three shares:

cd server && npm run recover-master

This reconstructs the master from two shares and re-seals it under the new host’s unlock, emitting a fresh BOUND_MASTER_CIPHERTEXT. Re-issue the license with a higher counter so the old host binding cannot be replayed.

Next