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
| What | How |
|---|---|
| Database | pg_dump piped through gzip (below) |
.env | copy it out-of-band — it holds DEV_MASTER_KEY |
db-data volume | the 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:
| Mode | Who 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. |
managed | the 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
- Keys and licensing config — the binding and escrow variables.
- Cryptography — envelope encryption and the key hierarchy.
- Upgrading — back up first; the backup is the rollback path.