Backups¶
The config is a git repository on the box (/var/lib/stronghold/config):
every change is a commit. That is the main backup: with a git remote, each
change goes off the box too, with its history, and a new box can start
from it. Backup files are for keeping a copy by hand, or a few on the box
to go back to.
Git¶
# stronghold.yaml
git:
remote: git@git.example.net:sites/home.git # SSH only, for now
branch: home # default: the host name
stronghold git key # make the box's SSH key; add it to the remote as a deploy key with write access
stronghold git # the remote, the branch, what's pushed, the last error
stronghold git push # push now (it pushes on its own within seconds of a change)
Each box pushes its own branch: the host name (or branch), and in a pair
<branch>/<node> (default pair/gw1, pair/gw2): the two histories
differ (the standby's commits are "Synced from gw1"), so they can't share
one. The push replaces the branch (--force): only this box pushes it.
The remote's host key is trusted on first use
(/var/lib/stronghold/git/known_hosts). A failing push is retried, less
often the longer it fails, and the System tab says so.
Secrets aren't in git: they stay on the box, and go in an encrypted backup file if wanted.
A new box from git: set its node first if it is one of a pair, make its key and add it to the remote, then
stronghold restore --git git@git.example.net:sites/home.git home
restores the branch's config as a change to confirm. The box's history starts from there (the remote keeps the old one).
What D14 also plans — the box applying signed commits pushed to the remote — isn't done yet.
Backup files¶
A backup is a .tar.gz: manifest.json (when, which box, which
version), config/ (the files), history.bundle (the git history:
git clone history.bundle reads it) and, if asked, secrets.enc: the
box's secrets (VPN keys, Wi-Fi passwords, the pairing key, the OIDC client
secret, the API tokens' hashes) encrypted with a passphrase
(PBKDF2-SHA256, 600 000 rounds, and AES-256-GCM). The secrets are never
in a backup unencrypted.
stronghold backup FILE # the config and its history
stronghold backup --secrets FILE # and the secrets (asks for a passphrase, or STRONGHOLD_PASSPHRASE)
stronghold restore [--dry-run] FILE # restore it: a change to confirm
stronghold restore --secrets FILE # with its secrets (asks for the passphrase)
Restoring is a change like any other: applied at once and rolled back unless confirmed within 60 seconds. The secrets it replaces come back if it is rolled back. The history isn't replayed: the restore is one commit ("Restored from the backup of gw1 (2026-10-16 12:00)"). A pair's backup restores on a box that is one of its nodes (set the node first); on the active box, the standby takes it by config sync.
Backups on the box¶
# stronghold.yaml
backups:
schedule: { days: [sun], at: "02:00" } # default every day at 03:30
before-update: true # one before each update
keep: 10 # the newest kept (default 10)
secrets: true # with the secrets
They are kept in /var/lib/stronghold/backups, named
stronghold-<host>-<UTC time>-<why>.tar.gz (scheduled, before-update,
by-hand). A scheduled backup missed by up to two hours (the box was off)
is still made. With secrets, the backup passphrase is a secret on the
box (set in the System tab, or the backups section's passphrase), so
backups can be made unattended: it protects the copies taken off the box,
not the box itself.
stronghold backups # the list
stronghold backups now # make one now
stronghold backups get NAME FILE # download one
stronghold backups delete NAME
stronghold restore --from NAME # restore one (the box's passphrase opens its secrets)
In the UI, the System tab has all of it: the list (download, restore, delete), "Make a backup now", downloading a backup file (with the secrets if a passphrase is given) and restoring one.