Skip to content

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.

Two roles, one module:

  • REST server: a host that declares the restic service in config.yaml. It stores repositories for all hosts and listens on the internal network.
  • Client: any host with enable = true that declares targets (backup destinations).
Diagram

Repository layout, by category:

<root>/<host>/system # "system" category (/ minus exclusions)
<root>/<host>/srv/nfs # "nfs" category (/srv/nfs/<...>)
<root>/<host>/srv/medias # "media" category (/srv/medias/<...>)
  1. In etc/config.yaml, add the restic service to the server host:

    services:
    restic:
  2. Attach the backup disk and mount it on /mnt/backup in the host configuration (usr/machines/<host>/), with the nofail option:

    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" ];
    };
  3. The repository is stored under serverDataDir (default /mnt/backup/restic). The firewall opens the REST port on the internal interface automatically.

Declare one or more targets in the host configuration:

darkone.service.restic = {
enable = true;
targets = [
{
# Remote repository via the zone REST server.
root = "rest:http://restic.zone.domain.tld:8888";
zone = "ag";
categories = [ "system" "nfs" ];
}
];
};

target fields:

FieldRole
nameIdentifier (default main), suffix for jobs/units.
rootLocal path or URL rest://.
zoneZone selecting the restic-password-<zone> passphrase.
categoriesData to back up: system, nfs, medias.

An idempotent recipe creates missing secrets (never overwrites):

Fenêtre de terminal
just passwd-restic

It generates, in usr/secrets/secrets.yaml:

  • one restic-password-<zone> passphrase per zone;
  • one restic/<host>/rest-password credential per fleet host.

By default, the server allows any authenticated host to access any repository. The enableServerPrivateRepos option enforces that each host can only access its own repository (prefix = hostname). Activate it in two steps to avoid any disruption:

  1. Deploy the entire fleet with per-host credentials (clients + servers), enableServerPrivateRepos = false. Verify that backups work.
  2. Set enableServerPrivateRepos = true and redeploy the servers.

On the target host, secrets are already in place. List then restore:

Fenêtre de terminal
# REST access variables (generated by sops) and repository passphrase.
set -a; . /run/secrets/rendered/restic-rest-env; set +a
export RESTIC_PASSWORD_FILE=/run/secrets/restic-password-ag
export RESTIC_REPOSITORY=rest:http://restic.zone.domain.tld:8888/$(hostname)/system
restic snapshots
restic restore latest --target /restore
  1. On a server, one account per fleet host in the htpasswd file:

    Fenêtre de terminal
    sudo cut -d: -f1 /run/restic-rest/htpasswd
  2. On a client, run an on-demand backup:

    Fenêtre de terminal
    systemctl start restic-backups-system-main.service
    journalctl -u restic-backups-system-main.service -e
  3. Isolation (if enableServerPrivateRepos = true): access to another host’s repository must be denied (401/403 error).