SpipCP
Backups

Storage nodes

Provision a backup-only box you own β€” it runs restic's rest-server in append-only mode, so a compromised site can add backups but never delete its own history. One dependency, enrolled like any other node.

A storage node is a backup-only box you own. You enroll it exactly like any other node β€” give SSH access β€” and SpipCP installs and configures one thing on it: restic's rest-server, run in append-only mode. Every site (and the panel itself) then backs up to that box, and the box's backup target appears in every backup-policy picker automatically.

The storage nodes tab
πŸ“·Backups β†’ Storage nodes: each box shows daemon health, append-only posture, capacity, and repo/snapshot count.img/storage-nodes.avif
Backups β†’ Storage nodes: each box shows daemon health, append-only posture, capacity, and repo/snapshot count.

Why rest-server (and not Garage / SeaweedFS / RustFS)

The whole point of a storage node is immutability: a site that gets compromised must be able to write a new backup but never delete an old one β€” so an attacker can't wipe the history and leave you with nothing to restore. That requires the storage server itself to refuse deletes on the backup credential.

rest-server does exactly that with its --append-only flag: the backup user can PUT new data but the server rejects every delete, server-side, no matter what the client asks. That's a hard guarantee, not a hope.

Why not an S3-compatible object store you self-host?

Self-hosted S3 stand-ins β€” Garage, SeaweedFS, RustFS β€” are great object stores, but none of them can enforce append-only / object-lock today. Without server-enforced immutability, a compromised client with the bucket key can delete objects, which defeats the purpose. So a self-hosted storage node runs rest-server, which can enforce it. (If you want object-store immutability, use a managed provider that supports object-lock β€” AWS S3 or Cloudflare R2 β€” see Bring your own storage.)

One dependency

rest-server is the only thing SpipCP installs on a storage node β€” a single static binary plus its systemd unit. No database, no object-store cluster, no second moving part to operate or patch. The box is "a disk with one daemon in front of it," which is what makes it cheap to run and easy to trust.

Add a storage node

Add a storage node enrolls a fresh VPS the same way adding a regular node does:

  1. Point it at a fresh box β€” give the host and SSH access. Any current Ubuntu LTS on a provider with a big disk works (this box is just storage).
  2. The panel installs and configures rest-server for you, in append-only mode, with two credentials: an append-only backup user (the lane every backup writes through) and a separate, loopback-only prune user (the only lane that can delete β€” used by the retention pruner, never by a backup). You never type a credential by hand.
  3. On success the box's backup target appears in every site's backup-policy picker and in the panel self-backup target β€” automatically.

Data disk β€” NVMe vs HDD (automatic)

Storage VPSes pair a small fast disk (the OS, often a 5–10 GB NVMe) with a big slow disk for data (the HDD β€” 500 GB, 1 TB, more). SpipCP puts each piece where it belongs, automatically:

  • The backups (the GBs) go on the big HDD β€” that's the whole point of the box.
  • rest-server's binary, its credentials file, and its working cache stay on the fast NVMe β€” small, hot, and they make every backup/restore faster.

You don't configure this up front. When the box boots and first reports in, SpipCP reads its disks and decides:

The Data disk card on a storage node
πŸ“·The node's Storage tab β†’ Data disk card: a raw 1 TB HDD was detected, so it offers β€œFormat & use for backups” β€” one click, with an explicit erase warning.img/storage-data-disk.avif
The node's Storage tab β†’ Data disk card: a raw 1 TB HDD was detected, so it offers β€œFormat & use for backups” β€” one click, with an explicit erase warning.
What it findsWhat happens
A big data disk already formatted + mountedThe repo moves onto it automatically β€” no clicks.
A big data disk that's raw (most providers ship the HDD unformatted)The storage node's Data disk card shows it and offers β€œFormat & use for backups.” You click, confirm, and SpipCP formats it (ext4), mounts it, and persists the mount across reboots.
One disk only (a small box)Backups live on the root disk β€” fine for a few sites.

Formatting always asks first

Formatting a disk erases it, so SpipCP never does it on its own β€” it only formats a disk you explicitly confirm on the Data disk card, and only a disk that is raw, unmounted, and not the OS disk (three independent safety checks refuse anything else). An already-mounted disk is used as-is, never wiped.

Which box β€” and how much space?

A storage node is disk-bound, not CPU-bound: restic is light (1 vCore / 2 GB RAM is plenty), so the only number that matters is how much backup history fits. Two things shrink that number in your favour:

  • Dedup. restic stores each unique chunk once. Backing up a site daily for a month is not 30Γ— its size β€” it's roughly its size plus the daily changes. Keeping lots of snapshots is cheap.
  • Compression + client-side encryption are built in; the repo is smaller than the raw files.

Rough per-thing footprint in the repo (after dedup, one site's full history):

WorkloadTypical repo footprint
Static sitea few hundred MB
WordPress~1–3 GB (uploads dominate; the DB is small)
PHP / Laravel app~1–2 GB
Node app~1 GB (most of node_modules dedups)
Docker app~2–8 GB (named volumes + image data)

So as a planning rule: sum each thing's unique data, multiply by ~1.3 for headroom, and ignore the snapshot count. A 500 GB box comfortably holds the backup history of ~30–60 WordPress sites (or ~20–40 Docker apps); a 1 TB box roughly doubles that. When a box gets close to full, the capacity bar on its health card goes amber, and the retention pruner (with its safety floor) keeps it in check.

The health card

Each storage node shows a health card so you can see the box is doing its job at a glance:

FieldWhat it tells you
DaemonIs rest-server up and reachable?
Append-onlyThe immutability posture β€” backups can add, never delete.
CapacityA bar of used / free disk, so you see the box filling up before it's full.
Repos / snapshotsHow many restic repositories and snapshots the box holds.
Last error / checkedThe last collection error (if any) and when the facts were last refreshed.

The two-credential split that makes the append-only lane real is explained in Append-only & immutability.

On this page