Skip to content

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.