backup · restore · migrate · clustered

Back it up. Restore it. Move it to another node.

ProxDock is a clustered Docker orchestrator built on ZFS. Each application gets its own dataset and its own address, and is backed up as a send stream that restores image, configuration and data as one unit. Migration between nodes runs that same code, so the path you need in an emergency is the one that runs on an ordinary Tuesday.

  • backups are zfs send streams, checksummed and verified
  • migration reuses restore, so the recovery path never goes stale
  • one static binary; any node serves the whole cluster
you@proxdock1 · install
$ wget -qO- https://proxdock.whydoesnothing.work/install.sh | sudo sh
==> host prepared
    zfs-2.3.9-0+deb13u1 · Docker version 29.8.0 · Compose v5.5.1
==> installing proxdock 0.67.0 (release)

 proxdock installed and running.
   Web UI:            https://10.42.0.11:8443
   Initial password:  GdYW-EQHX-VX6k-Hx8H

# everything after this happens in the browser:
#   the setup wizard, then pick a template and deploy.

Backups that restore

Stop, snapshot, start, then zfs send from the snapshot, so ownership, xattrs, ACLs, sparseness and recordsize all survive, because none of them are being re-derived on the way out. Every stream is checksummed and carries a self-describing manifest, and scheduled verification restores them on a timer rather than taking your word for it.

Migration is the recovery path

Moving an application to another node is the same code as backup and restore. Bulk data pre-seeds while it is still running; the cutover is a stop, an incremental send and a start. The address moves with it, and because the MAC is derived from the address, the rest of the network learns nothing new. The old dataset is kept for rollback.

The cluster has no special node

Every node serves the whole UI and API. Reads come from the local replica, writes go through the Raft leader, and node-local work is proxied one hop over mTLS with the original user asserted for audit. Lose a majority and the cluster goes read-only and says so, rather than failing one write at a time.

The interface

Dense where it has to be, and never guessing

One screen per job, master and detail, and a panel whose tabs each own their concern completely. Dense because an operator screen should be, and never asking you to guess what a control will do.

https://proxdock1.homelab.internal:8443/backups?tab=backups

Backups

6 scheduled jobs · 148 retained backups · 2.4 TB on tank/backups

Backups Jobs
Last night
6 / 6
all verified · 218 GB sent
Retention
148
7 daily · 4 weekly · 6 monthly
Target
2.4 TB
tank/backups · 61% used
Recent backups Back up now
applicationtakenkindsizestaterestore
immich 2026-09-05 03:00 full 184.2 GB verified Restore
paperless 2026-09-05 03:12 incremental 12.8 GB verified Restore
gitea 2026-09-05 03:18 incremental 4.1 GB verified Restore
jellyfin 2026-09-04 03:00 full 96.4 GB verified Restore
vaultwarden 2026-09-04 03:04 incremental 318 MB verified Restore
grafana 2026-09-03 03:00 full 1.9 GB stale Restore

Stop · snapshot · start · zfs send from the snapshot. Ownership, xattrs, ACLs, sparseness and recordsize survive; every stream is checksummed and carries a self-describing manifest.

Recovery

Restore is exercised continuously

Migration between nodes is the same code path as backup and restore, so the recovery path runs every time an application moves. Individual files can be browsed and pulled out of a snapshot without a full restore.

Getting something running

From an empty node to a running application

ProxDock is run from its web interface. The binary's command line is deliberately small: it covers the things you do on the host, and nothing you would do every day.

01

Pick a template

A searchable gallery of the 38 applications bundled with the binary. Or switch to the other tab and paste a Compose file of your own. The rest is identical either way.

02

Fill in what it needs

Each template names its settings with help text and defaults, and the storage it wants bound. Point a mount at a storage location and it uses your existing library rather than copying it.

03

Read the preview

Every change the transformation makes, as a path, a before and after, and the reason. Warnings are separated from refusals. Nothing has been written yet.

04

Deploy

The dataset is created, an address allocated, images pinned to the digests resolved right then, and a baseline snapshot taken once it is up. Backups can be scheduled from the same panel.

root@proxdock1 · the whole CLI
# proxdock help
proxdock — container orchestrator for Docker Compose on ZFS

  serve                run the node service (systemd entrypoint)
  recovery-password    mint a single-use login for this node (root only)
  version              print version

# proxdock recovery-password
  cbf3-8a1d-90e2-77bc
  single use · this node only · expires in 15 minutes
What it does

Backup, migration and the cluster, in detail

Send streams, not tarballs

Ownership, extended attributes, ACLs, sparseness and recordsize all survive, because a zfs send is not re-deriving any of them on the way out.

Restore keeps the old copy

A restore always receives into a fresh dataset and retains the previous one, so a restore that turns out to be wrong is a rollback rather than a loss.

Restore a copy beside the original

A restore can run under a new name and a new address, which is how you check a backup is good without touching the thing you are checking it against.

Scheduled restore verification

Backups are restored on a timer into throwaway clones. A backup that cannot be restored is a failure you want to hear about on a Wednesday, not during an outage.

Pull one file out of a snapshot

Browse a snapshot and extract individual files without a full restore. That is the common case for "I deleted one config file", and it needs no spare dataset.

Retention that includes the leftovers

Per-kind retention covers the datasets left behind by deletes and migrations too, so a cluster does not silently fill with orphans nobody is tracking.

The address moves with the application

Each application holds a real address on a network zone, with a MAC derived from it, so ARP caches and switch tables survive a migration between nodes.

Pre-seed, then a short cutover

Bulk data copies while the application is still serving. The stop lasts an incremental send, not the length of the transfer, and the previous dataset is kept for rollback.

Joining is one token

Short-lived, single-use, and issuing it also issues the joining node’s cluster-internal mTLS certificate. Nothing to distribute and nothing to copy between hosts by hand.

Quorum is stated, not inferred

A lost majority makes the cluster read-only and says so in a banner. Applications on a merely unreachable node keep running; they just cannot be managed until it is back.

38 templates in the binary

Embedded rather than fetched, so a template is always version-matched to the ProxDock deploying it. Each names the settings it needs and the storage it wants bound.

Docker is not replaced

ProxDock writes the files and runs docker compose. Nothing it deploys is unreachable by the ordinary tools, and nothing is trapped behind the browser.

Pre-1.0

Working, and running a real cluster

The full lifecycle runs on a live three-node cluster: deploy, update, roll back, snapshot, back up, restore, migrate between nodes. Remote backup targets can be configured and tested but do not upload yet. That is the next major piece.