{ pkgs, config, lib, ... }: let cfg = config.sbruder.restic.system; sftpTarget = "u313368-sub4@u313368-sub4.your-storagebox.de"; sftpPort = 23; repository = "sftp://${sftpTarget}:${toString sftpPort}/personal"; excludes = [ # Caches "/home/*/Downloads/" "/home/*/.cache/" "/home/*/**/cache/" "/home/*/.local/share/Trash" # some gui applications use it "/root/.cache" "/data/cache/" "/var/cache/" # Rust "/home/*/.rustup/toolchains/" "/home/*/.cargo" # Misc "/home/*/mount" "/home/*/mounts" # Docker (state should be kept somewhere else) "/var/lib/docker/" # Static configuration (generated from this repository) "/etc/static/" ] ++ cfg.extraExcludes; excludesFile = pkgs.writeText "excludes.txt" (lib.concatStringsSep "\n" excludes); # script to use restic as user without dealing with authentication authScript = pkgs.writeShellScriptBin "restic-auth" '' ${pkgs.restic}/bin/restic \ --password-command="pass data/backup/restic-nixos" \ --repo "${repository}" \ $@ ''; in { options.sbruder.restic.system = { enable = lib.mkEnableOption "restic"; timerConfig = lib.mkOption { type = with lib.types; attrsOf str; default = { OnCalendar = "18:00"; RandomizedDelaySec = "2h"; }; }; extraPaths = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; example = [ "/data" ]; }; extraExcludes = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; }; uploadLimit = lib.mkOption { type = lib.types.nullOr lib.types.int; default = 1500; }; prune = lib.mkEnableOption "pruning"; }; config = lib.mkIf cfg.enable { sops.secrets = { restic-password = { }; restic-repository = { }; } // lib.optionalAttrs cfg.prune { restic-ssh-key = { sopsFile = ../../machines/${config.networking.hostName}/secrets.yaml; }; }; services.restic.backups.system = { inherit (cfg) timerConfig; repositoryFile = config.sops.secrets.restic-repository.path; passwordFile = config.sops.secrets.restic-password.path; paths = [ "/etc" "/home" "/root" "/srv" "/var" ] ++ cfg.extraPaths; extraBackupArgs = [ "--exclude-caches" "--exclude-file=${excludesFile}" "--tag system" "--verbose" ] ++ lib.optional (cfg.uploadLimit != null) "--limit-upload=${toString cfg.uploadLimit}"; }; systemd.services."restic-backups-system".serviceConfig = { "Nice" = 10; "IOSchedulingClass" = "best-effort"; "IOSchedulingPriority" = 7; }; services.restic.backups.system-prune = lib.mkIf cfg.prune { inherit repository; passwordFile = config.sops.secrets.restic-password.path; timerConfig = { OnCalendar = "*-1/2-07 03:00:00"; RandomizedDelaySec = "4h"; }; paths = [ ]; extraOptions = [ "-o" "sftp.command='ssh -i ${config.sops.secrets.restic-ssh-key.path} -p ${toString sftpPort} ${sftpTarget} -s sftp'" ]; pruneOpts = [ "--keep-daily 7" "--keep-monthly 12" "--keep-weekly 5" "--keep-yearly 10" "--tag system" "--verbose" ] ++ lib.optional (cfg.uploadLimit != null) "--limit-upload=${toString cfg.uploadLimit}"; }; environment.systemPackages = [ authScript ]; }; }