{ config, lib, pkgs, ... }: with lib; let cfg = config.services.borgbackup; targetOptions = { options = { repo = mkOption { type = types.str; }; paths = mkOption { type = types.listOf types.str; default = []; }; prune = mkOption { type = types.attrsOf (types.listOf types.str); default = {}; }; interval = mkOption { type = types.str; default = "6h"; }; lock = mkOption { type = types.nullOr types.str; default = "backup"; }; network = mkOption { type = types.bool; default = true; }; }; }; in { options = { services.borgbackup = { snapshots = mkOption { type = types.nullOr (types.enum ["btrfs"]); default = null; }; targets = mkOption { type = types.attrsOf (types.submodule targetOptions); default = {}; }; prefix = mkOption { type = types.str; }; }; }; config = mkIf (any (t: t.paths != []) (attrValues cfg.targets)) { services.btrfs-snapshots.enable = mkIf (cfg.snapshots == "btrfs") true; systemd.timers = (listToAttrs (map ({ target, path, tCfg }: nameValuePair "borgbackup-${target}-${path}" { wantedBy = [ "timers.target" ]; timerConfig = { Persistent = false; OnBootSec = tCfg.interval; OnUnitInactiveSec = tCfg.interval; }; }) (flatten (mapAttrsToList (target: tCfg: map (path: { inherit target path tCfg; }) tCfg.paths) cfg.targets)))) // (mapAttrs' (target: tCfg: nameValuePair "borgbackup-prune-${target}" { wantedBy = [ "timers.target" ]; timerConfig = { Persistent = false; OnBootSec = tCfg.interval; OnUnitInactiveSec = tCfg.interval; }; }) cfg.targets); systemd.services = (mapAttrs' (target: tCfg: nameValuePair "borgbackup-${target}@" (let deps = flatten [ (optional (cfg.snapshots == "btrfs") "btrfs-snapshot@%p.service") (optional tCfg.network "network-online.target") ]; in { bindsTo = deps; after = deps; path = with pkgs; [borgbackup] ++ optional (tCfg.lock != null) utillinux; script = let borgCmd = '' borg create \ --lock-wait 600 \ --stats \ --list \ --filter 'AME' \ --exclude-caches \ --keep-exclude-tags \ --patterns-from .backup \ --one-file-system \ --compression auto,lzma \ ${tCfg.repo}::${cfg.prefix}$1-{utcnow} ''; in if tCfg.lock == null then borgCmd else "flock -xo /var/lock/${tCfg.lock} ${borgCmd}"; scriptArgs = "%i"; unitConfig = { AssertPathIsDirectory = mkIf (tCfg.lock != null) "/var/lock"; }; serviceConfig = { Type = "oneshot"; WorkingDirectory = if (cfg.snapshots == null) then "%p" else "/mnt/snapshot-%i"; Nice = 15; IOSchedulingClass = 2; IOSchedulingPriority = 7; SuccessExitStatus = [1 2]; }; })) cfg.targets) // (mapAttrs' (target: tCfg: nameValuePair "borgbackup-prune-${target}" { bindsTo = ["network-online.target"]; after = ["network-online.target"]; path = with pkgs; [borgbackup]; script = concatStringsSep "\n" (mapAttrsToList (path: args: '' borg prune \ --list \ --stats \ --prefix "${cfg.prefix}${path}" \ ${concatStringsSep " " args} \ ${tCfg.repo} '') tCfg.prune); serviceConfig = { Type = "oneshot"; }; }) cfg.targets); }; }