Network backups (restic)
The darkone.service.restic module backs up fleet hosts with
restic 🡕: each host (client) pushes its data to a
repository, either local or remote via a zone REST server.
Principles
Section titled “Principles”Two roles, one module:
- REST server: a host that declares the
resticservice inconfig.yaml. It stores all hosts’ repositories and listens on the internal network. - Client: any host with
enable = truethat declarestargets(backup destinations).
Repository layout: one repository per category, named after it.
<root>/<host>/system # category "system" (/ minus exclusions)<root>/<host>/nfs # category "nfs" (/srv/nfs/<...>)<root>/<host>/medias # category "medias" (/srv/medias/<...>)Setting up a backup server
Section titled “Setting up a backup server”-
In
etc/config.yaml, add theresticservice to the server host:services:restic: -
Plug in the backup disk and mount it on
/mnt/backupin the host configuration (usr/machines/<host>/), with thenofailoption:fileSystems."/mnt/backup" = {device = "/dev/disk/by-uuid/<uuid>";fsType = "ext4";options = [ "nofail" ];};# The REST server only starts once the disk is mounted.systemd.services.restic-rest-server = {requires = [ "mnt-backup.mount" ];after = [ "mnt-backup.mount" ];}; -
The repository is stored under
serverDataDir(default/mnt/backup/restic). The firewall opens the REST port on the internal interface automatically. -
If other zone clients need to access it via the tailnet, add
listenAll: by default the server only listens on the host’s LAN address, invisible from the VPN.darkone.service.restic = {enable = true;enableServer = true;listenAll = true;};
Adding a client
Section titled “Adding a client”Declare one or more targets in the host configuration:
darkone.service.restic = { enable = true; targets = [ { # Remote repository via the zone's REST server. root = "rest:http://restic.home.example.net:8888"; zone = "home"; categories = [ "system" "nfs" ]; } ];};target fields:
| Field | Role |
|---|---|
name | Identifier (default main), suffix for jobs/units. |
root | Local path or rest:// URL. |
zone | Zone that selects the restic-password-<zone> passphrase. |
categories | Data to back up: system, nfs, medias. |
Off-site backup
Section titled “Off-site backup”A second target pointing to another zone’s REST server provides the “1” in the
3-2-1 rule. Two precautions specific to off-site backups.
Address the server by its tailnet IP, never by its zone name:
{ name = "offsite"; root = "rest:http://100.64.0.20:8888"; zone = "home"; categories = [ "system" ];}A name like restic.<remote-zone>.example.net is only resolved by that zone’s
gateway. If the remote site goes down, the resolver goes down with it and the
failure appears as a DNS resolution error, which misleads diagnostics. The
tailnet IP removes this indirection, along with the dependency on the remote
site’s subnet route. It requires listenAll on the server side (see above).
Generate passwords
Section titled “Generate passwords”The generation of internal secrets is idempotent: it creates what is missing, never overwrites.
just configure-admin-hostIt generates, in usr/secrets/secrets.yaml:
- a
restic-password-<zone>passphrase per zone; - a
restic/<host>/rest-passwordcredential per host in the fleet.
Running a manual backup
Section titled “Running a manual backup”The module creates one systemd service per target × category pair, named
restic-backups-<category>-<name> (the <name> comes from the target’s name
field, main by default). Examples: restic-backups-system-main,
restic-backups-nfs-main.
-
List the backup services present on the host:
Fenêtre de terminal systemctl list-units 'restic-backups-*' -
Trigger an immediate backup without waiting for the timer:
Fenêtre de terminal sudo systemctl start restic-backups-system-main.service -
Follow its progress in real time:
Fenêtre de terminal journalctl -fu restic-backups-system-main.service
Repository isolation
Section titled “Repository isolation”The REST server runs with private repositories (privateRepos = true): each host
only accesses the repository whose path starts with its own hostname. The
module automatically aligns the REST account (<hôte>) with the repository prefix
(<racine>/<hôte>/...), so isolation is permanent → no configuration needed.
An attempt to access another host’s repository returns a 403 error.
Restore
Section titled “Restore”On the host in question, the secrets are already in place. List and restore:
# Adapter la zone du dépôt (et la catégorie : system, nfs, medias).ZONE=home
# Variables d'accès REST (générées par sops) et passphrase de dépôt.set -a; . /run/secrets/rendered/restic-rest-env; set +aexport RESTIC_PASSWORD_FILE=/run/secrets/restic-password-$ZONEexport RESTIC_REPOSITORY=rest:http://restic.$ZONE.example.net:8888/$(hostname)/system
restic snapshotsrestic restore latest --target /restoreVerifying
Section titled “Verifying”-
On a server, one account per fleet host in the htpasswd file:
Fenêtre de terminal sudo cut -d: -f1 /run/restic-rest/htpasswd -
On a client, run a manual backup (see above) and check that it completes without errors.
-
Isolation: from a client, accessing another host’s repository must be denied (401/403 error).