Wheelhouse docs

Take a backup that is actually complete#

You will end up with one encrypted archive, off the router, that can rebuild the box: its configuration, its accounts and their password hashes, its API tokens, its two-factor enrolments, its audit log, its licence, its TLS certificate and its commit history.

The important sentence first. The configuration file is not a backup. The Download config button in the UI exports config.boot and nothing else. A restore from that file produces a working router that nobody can log in to, because every account lives outside it.

Before you start#

  • Shell access to the router — the console, or SSH. SSH is not part of the default configuration; see Turn SSH on, and restrict it, or do this at the console with a USB stick.
  • Somewhere to keep the result that has the same access control as the router itself.
  • Something to encrypt with: age, gpg, or your backup system's own encryption.

What a backup has to contain#

All of /config. That is the whole answer, and it is why the recipe is a single tar.

PathWhat is lost without it
/config/config.bootThe router's entire configuration: interfaces, firewall, NAT, DHCP, DNS, routing, containers. Also the VyOS API key the agent authenticates with, under service https api keys.
/config/wheelhouse/state.jsonEvery account and its Argon2id password hash, TOTP secrets, API token hashes, live sessions, agent settings and the licence key. Mode 0600 — it is a credential file.
/config/wheelhouse/audit.jsonlThe audit log: who changed what, from where, with which role, plus audit.jsonl.1 after a rotation. It moved out of state.json into its own file; entries in an older state.json are migrated into it at start-up.
/config/wheelhouse/api-keyThe agent's copy of that same VyOS API key. It must match what config.boot holds, or the agent cannot read the router.
/config/wheelhouse/agent.crt, agent.keyThe TLS certificate. Regenerated automatically if absent, at the cost of a fresh browser warning for everyone.
/config/wheelhouse/license-keyThe licence, when it was placed as a file rather than pasted into the UI. Pasted keys live in state.json.
/config/wheelhouse/agent.yamlThe desired-state file, if you use the reconcile loop.
/config/archive/VyOS' numbered configuration revisions — the history behind the Restore buttons on the System page. Not needed to run; needed to go back.
/config/scripts, /config/auth, /config/user-dataAnything you or VyOS put there: task scripts, PKI material, keys.

Step 1 — Take it#

From the console, or over SSH as vyos:

bash
sudo tar -czf /tmp/wheelhouse-$(hostname)-$(date +%F).tgz -C / config

Then copy it off and remove it from the router:

bash
scp wheelhouse@<router>:/tmp/wheelhouse-*.tgz .
ssh  wheelhouse@<router> 'sudo rm -f /tmp/wheelhouse-*.tgz'

Or in one step, when sudo on the router does not prompt:

bash
ssh wheelhouse@<router> 'sudo tar -czf - -C / config' > wheelhouse-$(date +%F).tgz

Step 2 — Encrypt it#

bash
age -p -o wheelhouse-2026-09-02.tgz.age wheelhouse-2026-09-02.tgz
# or
gpg --symmetric --cipher-algo AES256 wheelhouse-2026-09-02.tgz

Then remove the plaintext archive.

Step 3 — On a schedule#

There is no scheduler in Wheelhouse and no off-box backup target — no SFTP, no S3, no git. Run the same command from cron on another machine, so the schedule does not live on the thing you are protecting:

crontab on the backup host
# 03:15 daily
15 3 * * *  ssh wheelhouse@edge 'sudo tar -czf - -C / config' | age -r <recipient> > /backups/edge-$(date +\%F).tgz.age

Take one before every upgrade and before every large configuration change, on top of whatever runs nightly.

Check it worked#

A backup you have never opened is a hypothesis. Two checks, in increasing order of value.

Every time — look for the three files that matter:

bash
tar -tzf wheelhouse-2026-09-02.tgz | grep -E 'config/(config.boot|wheelhouse/state.json|wheelhouse/api-key)$'

Three lines means you have a backup. Fewer means you have a file.

Once, before you need it — restore onto a VM. Follow Rebuild a dead router onto a new box against a throwaway machine and sign in with your real admin account. That is the only check that proves the whole chain.

The configuration on its own#

Useful for diffing, for version control, and for --check-against. It is not a substitute for the tarball.

  • From the UI: the Download config button in the System page header. Saves <hostname>-config.boot. Admin only, because the file is unredacted.
  • From the API:

    bash
    R=https://<router>:8443
    T=wh_...
    curl -sk -H "Authorization: Bearer $T" "$R/api/config/raw" \
      | python3 -c 'import json,sys; sys.stdout.write(json.load(sys.stdin)["config"])' > config.boot
  • As set commands, which is easier to read in a diff and the shape the installer and the importer consume:

    bash
    curl -sk -H "Authorization: Bearer $T" "$R/api/config/commands"

    Below the admin role this response is redacted: private keys, pre-shared secrets and password hashes are blanked.

  • On the router: POST /api/config/save with {"file": "/config/backup.boot"} writes a copy on the box. That is a snapshot, not a backup — it is on the disk you are protecting against.

What is missing#

Stated plainly, because you will find out anyway:

  • No one-click backup. The UI exports config.boot and nothing else. There is no archive of config.boot plus state.json, and no support bundle.
  • No restore-from-file in the product. No upload endpoint, no file picker. Every restore from off the box goes through scp and a shell — Restore a configuration.
  • No scheduled or off-box backup. No SFTP, S3 or git target, no timer.
  • No factory reset. There is no command and no UI action that wipes accounts and returns the box to a shipped configuration. Starting over means reinstalling from the ISO.
  • No self-service licence release when a router is retired — Rebuild a dead router explains what that costs.

Undoing it#

Delete the archive, securely. shred -u on Linux, or whatever your storage offers.

See also#


Checked against docs/backup-restore.md · agent/store.go · agent/license.go · docs/security.md

Updated 2026-09-02 backup operations credentials