{ config, lib, pkgs, ... }: with lib; { options.services.restic.backups = mkOption { description = '' Periodic backups to create with Restic. ''; type = types.attrsOf (types.submodule ({ name, config, ... }: { options = { passwordFile = mkOption { type = types.str; description = '' Read the repository password from a file. ''; example = "/etc/nixos/restic-password"; }; repository = mkOption { type = types.str; description = '' repository to backup to. ''; example = "sftp:backup@192.168.1.100:/backups/${name}"; }; paths = mkOption { type = types.listOf types.str; default = []; description = '' Which paths to backup. ''; example = [ "/var/lib/postgresql" "/home/user/backup" ]; }; timerConfig = mkOption { type = types.attrsOf types.str; default = { OnCalendar = "daily"; }; description = '' When to run the backup. See man systemd.timer for details. ''; example = { OnCalendar = "00:05"; RandomizedDelaySec = "5h"; }; }; user = mkOption { type = types.str; default = "root"; description = '' As which user the backup should run. ''; example = "postgresql"; }; extraBackupArgs = mkOption { type = types.listOf types.str; default = []; description = '' Extra arguments passed to restic backup. ''; example = [ "--exclude-file=/etc/nixos/restic-ignore" ]; }; extraOptions = mkOption { type = types.listOf types.str; default = []; description = '' Extra extended options to be passed to the restic --option flag. ''; example = [ "sftp.command='ssh backup@192.168.1.100 -i /home/user/.ssh/id_rsa -s sftp'" ]; }; initialize = mkOption { type = types.bool; default = false; description = '' Create the repository if it doesn't exist. ''; }; }; })); default = {}; example = { localbackup = { paths = [ "/home" ]; repository = "/mnt/backup-hdd"; passwordFile = "/etc/nixos/secrets/restic-password"; initialize = true; }; remotebackup = { paths = [ "/home" ]; repository = "sftp:backup@host:/backups/home"; passwordFile = "/etc/nixos/secrets/restic-password"; extraOptions = [ "sftp.command='ssh backup@host -i /etc/nixos/secrets/backup-private-key -s sftp'" ]; timerConfig = { OnCalendar = "00:05"; RandomizedDelaySec = "5h"; }; }; }; }; config = { systemd.services = mapAttrs' (name: backup: let extraOptions = concatMapStrings (arg: " -o ${arg}") backup.extraOptions; connectTo = elemAt (splitString ":" backup.repository) 1; resticCmd = "${pkgs.restic}/bin/restic${extraOptions}"; in nameValuePair "restic-backups-${name}" ({ environment = { RESTIC_PASSWORD_FILE = backup.passwordFile; RESTIC_REPOSITORY = backup.repository; }; path = with pkgs; [ openssh ]; restartIfChanged = false; serviceConfig = { Type = "oneshot"; ExecStart = "${resticCmd} backup ${concatStringsSep " " backup.extraBackupArgs} ${concatStringsSep " " backup.paths}"; User = backup.user; }; } // optionalAttrs backup.initialize { preStart = '' ${resticCmd} snapshots || ${resticCmd} init ''; }) ) config.services.restic.backups; systemd.timers = mapAttrs' (name: backup: nameValuePair "restic-backups-${name}" { wantedBy = [ "timers.target" ]; timerConfig = backup.timerConfig; }) config.services.restic.backups; }; }