Loading nixos/modules/services/backup/restic.nix +396 −339 Original line number Diff line number Diff line { config, lib, pkgs, utils, ... }: { config, lib, pkgs, utils, ... }: let # Type for a valid systemd unit option. Needed for correctly passing "timerConfig" to "systemd.timers" inherit (utils.systemdUtils.unitOptions) unitOption; Loading @@ -8,7 +14,10 @@ in description = '' Periodic backups to create with Restic. ''; type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: { type = lib.types.attrsOf ( lib.types.submodule ( { name, ... }: { options = { passwordFile = lib.mkOption { type = lib.types.str; Loading @@ -28,7 +37,14 @@ in }; rcloneOptions = lib.mkOption { type = with lib.types; nullOr (attrsOf (oneOf [ str bool ])); type = with lib.types; nullOr ( attrsOf (oneOf [ str bool ]) ); default = null; description = '' Options to pass to rclone to control its behavior. Loading @@ -45,7 +61,14 @@ in }; rcloneConfig = lib.mkOption { type = with lib.types; nullOr (attrsOf (oneOf [ str bool ])); type = with lib.types; nullOr ( attrsOf (oneOf [ str bool ]) ); default = null; description = '' Configuration for the rclone remote being used for backup. Loading Loading @@ -269,8 +292,19 @@ in having to manually specify most options. ''; }; progressFps = lib.mkOption { type = with lib.types; nullOr numbers.nonnegative; default = null; description = '' Controls the frequency of progress reporting. ''; example = 0.1; }; })); }; } ) ); default = { }; example = { localbackup = { Loading Loading @@ -300,9 +334,8 @@ in assertion = (v.repository == null) != (v.repositoryFile == null); message = "services.restic.backups.${n}: exactly one of repository or repositoryFile should be set"; }) config.services.restic.backups; systemd.services = lib.mapAttrs' (name: backup: systemd.services = lib.mapAttrs' ( name: backup: let extraOptions = lib.concatMapStrings (arg: " -o ${arg}") backup.extraOptions; inhibitCmd = lib.concatStringsSep " " [ Loading @@ -313,7 +346,9 @@ in "--why=${lib.escapeShellArg "Scheduled backup ${name}"} " ]; resticCmd = "${lib.optionalString backup.inhibitsSleep inhibitCmd}${backup.package}/bin/restic${extraOptions}"; excludeFlags = lib.optional (backup.exclude != []) "--exclude-file=${pkgs.writeText "exclude-patterns" (lib.concatStringsSep "\n" backup.exclude)}"; excludeFlags = lib.optional ( backup.exclude != [ ] ) "--exclude-file=${pkgs.writeText "exclude-patterns" (lib.concatStringsSep "\n" backup.exclude)}"; filesFromTmpFile = "/run/restic-backups-${name}/includes"; doBackup = (backup.dynamicFilesFrom != null) || (backup.paths != null && backup.paths != [ ]); pruneCmd = lib.optionals (builtins.length backup.pruneOpts > 0) [ Loading @@ -328,41 +363,58 @@ in rcloneAttrToConf = v: "RCLONE_CONFIG_" + lib.toUpper (rcloneRemoteName + "_" + v); toRcloneVal = v: if lib.isBool v then lib.boolToString v else v; in lib.nameValuePair "restic-backups-${name}" ({ environment = { lib.nameValuePair "restic-backups-${name}" ( { environment = { # not %C, because that wouldn't work in the wrapper script RESTIC_CACHE_DIR = "/var/cache/restic-backups-${name}"; RESTIC_PASSWORD_FILE = backup.passwordFile; RESTIC_REPOSITORY = backup.repository; RESTIC_REPOSITORY_FILE = backup.repositoryFile; } // lib.optionalAttrs (backup.rcloneOptions != null) (lib.mapAttrs' (name: value: lib.nameValuePair (rcloneAttrToOpt name) (toRcloneVal value) } // lib.optionalAttrs (backup.rcloneOptions != null) ( lib.mapAttrs' ( name: value: lib.nameValuePair (rcloneAttrToOpt name) (toRcloneVal value) ) backup.rcloneOptions ) backup.rcloneOptions) // lib.optionalAttrs (backup.rcloneConfigFile != null) { // lib.optionalAttrs (backup.rcloneConfigFile != null) { RCLONE_CONFIG = backup.rcloneConfigFile; } // lib.optionalAttrs (backup.rcloneConfig != null) (lib.mapAttrs' (name: value: lib.nameValuePair (rcloneAttrToConf name) (toRcloneVal value) } // lib.optionalAttrs (backup.rcloneConfig != null) ( lib.mapAttrs' ( name: value: lib.nameValuePair (rcloneAttrToConf name) (toRcloneVal value) ) backup.rcloneConfig ) backup.rcloneConfig); // lib.optionalAttrs (backup.progressFps != null) { RESTIC_PROGRESS_FPS = toString backup.progressFps; }; path = [ config.programs.ssh.package ]; restartIfChanged = false; wants = [ "network-online.target" ]; after = [ "network-online.target" ]; serviceConfig = { serviceConfig = { Type = "oneshot"; ExecStart = (lib.optionals doBackup [ "${resticCmd} backup ${lib.concatStringsSep " " (backup.extraBackupArgs ++ excludeFlags)} --files-from=${filesFromTmpFile}" ]) ++ pruneCmd ++ checkCmd; ExecStart = (lib.optionals doBackup [ "${resticCmd} backup ${ lib.concatStringsSep " " (backup.extraBackupArgs ++ excludeFlags) } --files-from=${filesFromTmpFile}" ]) ++ pruneCmd ++ checkCmd; User = backup.user; RuntimeDirectory = "restic-backups-${name}"; CacheDirectory = "restic-backups-${name}"; CacheDirectoryMode = "0700"; PrivateTmp = true; } // lib.optionalAttrs (backup.environmentFile != null) { } // lib.optionalAttrs (backup.environmentFile != null) { EnvironmentFile = backup.environmentFile; }; } // lib.optionalAttrs (backup.initialize || doBackup || backup.backupPrepareCommand != null) { } // lib.optionalAttrs (backup.initialize || doBackup || backup.backupPrepareCommand != null) { preStart = '' ${lib.optionalString (backup.backupPrepareCommand != null) '' ${pkgs.writeScript "backupPrepareCommand" backup.backupPrepareCommand} Loading @@ -377,7 +429,8 @@ in ${pkgs.writeScript "dynamicFilesFromScript" backup.dynamicFilesFrom} >> ${filesFromTmpFile} ''} ''; } // lib.optionalAttrs (doBackup || backup.backupCleanupCommand != null) { } // lib.optionalAttrs (doBackup || backup.backupCleanupCommand != null) { postStop = '' ${lib.optionalString (backup.backupCleanupCommand != null) '' ${pkgs.writeScript "backupCleanupCommand" backup.backupCleanupCommand} Loading @@ -386,22 +439,25 @@ in rm ${filesFromTmpFile} ''} ''; }) } ) config.services.restic.backups; systemd.timers = lib.mapAttrs' (name: backup: lib.nameValuePair "restic-backups-${name}" { ) config.services.restic.backups; systemd.timers = lib.mapAttrs' ( name: backup: lib.nameValuePair "restic-backups-${name}" { wantedBy = [ "timers.target" ]; timerConfig = backup.timerConfig; }) (lib.filterAttrs (_: backup: backup.timerConfig != null) config.services.restic.backups); } ) (lib.filterAttrs (_: backup: backup.timerConfig != null) config.services.restic.backups); # generate wrapper scripts, as described in the createWrapper option environment.systemPackages = lib.mapAttrsToList (name: backup: let environment.systemPackages = lib.mapAttrsToList ( name: backup: let extraOptions = lib.concatMapStrings (arg: " -o ${arg}") backup.extraOptions; resticCmd = "${backup.package}/bin/restic${extraOptions}"; in pkgs.writeShellScriptBin "restic-${name}" '' in pkgs.writeShellScriptBin "restic-${name}" '' set -a # automatically export variables ${lib.optionalString (backup.environmentFile != null) "source ${backup.environmentFile}"} # set same environment variables as the systemd service Loading @@ -413,6 +469,7 @@ in PATH=${config.systemd.services."restic-backups-${name}".environment.PATH}:$PATH exec ${resticCmd} "$@" '') (lib.filterAttrs (_: v: v.createWrapper) config.services.restic.backups); '' ) (lib.filterAttrs (_: v: v.createWrapper) config.services.restic.backups); }; } Loading
nixos/modules/services/backup/restic.nix +396 −339 Original line number Diff line number Diff line { config, lib, pkgs, utils, ... }: { config, lib, pkgs, utils, ... }: let # Type for a valid systemd unit option. Needed for correctly passing "timerConfig" to "systemd.timers" inherit (utils.systemdUtils.unitOptions) unitOption; Loading @@ -8,7 +14,10 @@ in description = '' Periodic backups to create with Restic. ''; type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: { type = lib.types.attrsOf ( lib.types.submodule ( { name, ... }: { options = { passwordFile = lib.mkOption { type = lib.types.str; Loading @@ -28,7 +37,14 @@ in }; rcloneOptions = lib.mkOption { type = with lib.types; nullOr (attrsOf (oneOf [ str bool ])); type = with lib.types; nullOr ( attrsOf (oneOf [ str bool ]) ); default = null; description = '' Options to pass to rclone to control its behavior. Loading @@ -45,7 +61,14 @@ in }; rcloneConfig = lib.mkOption { type = with lib.types; nullOr (attrsOf (oneOf [ str bool ])); type = with lib.types; nullOr ( attrsOf (oneOf [ str bool ]) ); default = null; description = '' Configuration for the rclone remote being used for backup. Loading Loading @@ -269,8 +292,19 @@ in having to manually specify most options. ''; }; progressFps = lib.mkOption { type = with lib.types; nullOr numbers.nonnegative; default = null; description = '' Controls the frequency of progress reporting. ''; example = 0.1; }; })); }; } ) ); default = { }; example = { localbackup = { Loading Loading @@ -300,9 +334,8 @@ in assertion = (v.repository == null) != (v.repositoryFile == null); message = "services.restic.backups.${n}: exactly one of repository or repositoryFile should be set"; }) config.services.restic.backups; systemd.services = lib.mapAttrs' (name: backup: systemd.services = lib.mapAttrs' ( name: backup: let extraOptions = lib.concatMapStrings (arg: " -o ${arg}") backup.extraOptions; inhibitCmd = lib.concatStringsSep " " [ Loading @@ -313,7 +346,9 @@ in "--why=${lib.escapeShellArg "Scheduled backup ${name}"} " ]; resticCmd = "${lib.optionalString backup.inhibitsSleep inhibitCmd}${backup.package}/bin/restic${extraOptions}"; excludeFlags = lib.optional (backup.exclude != []) "--exclude-file=${pkgs.writeText "exclude-patterns" (lib.concatStringsSep "\n" backup.exclude)}"; excludeFlags = lib.optional ( backup.exclude != [ ] ) "--exclude-file=${pkgs.writeText "exclude-patterns" (lib.concatStringsSep "\n" backup.exclude)}"; filesFromTmpFile = "/run/restic-backups-${name}/includes"; doBackup = (backup.dynamicFilesFrom != null) || (backup.paths != null && backup.paths != [ ]); pruneCmd = lib.optionals (builtins.length backup.pruneOpts > 0) [ Loading @@ -328,41 +363,58 @@ in rcloneAttrToConf = v: "RCLONE_CONFIG_" + lib.toUpper (rcloneRemoteName + "_" + v); toRcloneVal = v: if lib.isBool v then lib.boolToString v else v; in lib.nameValuePair "restic-backups-${name}" ({ environment = { lib.nameValuePair "restic-backups-${name}" ( { environment = { # not %C, because that wouldn't work in the wrapper script RESTIC_CACHE_DIR = "/var/cache/restic-backups-${name}"; RESTIC_PASSWORD_FILE = backup.passwordFile; RESTIC_REPOSITORY = backup.repository; RESTIC_REPOSITORY_FILE = backup.repositoryFile; } // lib.optionalAttrs (backup.rcloneOptions != null) (lib.mapAttrs' (name: value: lib.nameValuePair (rcloneAttrToOpt name) (toRcloneVal value) } // lib.optionalAttrs (backup.rcloneOptions != null) ( lib.mapAttrs' ( name: value: lib.nameValuePair (rcloneAttrToOpt name) (toRcloneVal value) ) backup.rcloneOptions ) backup.rcloneOptions) // lib.optionalAttrs (backup.rcloneConfigFile != null) { // lib.optionalAttrs (backup.rcloneConfigFile != null) { RCLONE_CONFIG = backup.rcloneConfigFile; } // lib.optionalAttrs (backup.rcloneConfig != null) (lib.mapAttrs' (name: value: lib.nameValuePair (rcloneAttrToConf name) (toRcloneVal value) } // lib.optionalAttrs (backup.rcloneConfig != null) ( lib.mapAttrs' ( name: value: lib.nameValuePair (rcloneAttrToConf name) (toRcloneVal value) ) backup.rcloneConfig ) backup.rcloneConfig); // lib.optionalAttrs (backup.progressFps != null) { RESTIC_PROGRESS_FPS = toString backup.progressFps; }; path = [ config.programs.ssh.package ]; restartIfChanged = false; wants = [ "network-online.target" ]; after = [ "network-online.target" ]; serviceConfig = { serviceConfig = { Type = "oneshot"; ExecStart = (lib.optionals doBackup [ "${resticCmd} backup ${lib.concatStringsSep " " (backup.extraBackupArgs ++ excludeFlags)} --files-from=${filesFromTmpFile}" ]) ++ pruneCmd ++ checkCmd; ExecStart = (lib.optionals doBackup [ "${resticCmd} backup ${ lib.concatStringsSep " " (backup.extraBackupArgs ++ excludeFlags) } --files-from=${filesFromTmpFile}" ]) ++ pruneCmd ++ checkCmd; User = backup.user; RuntimeDirectory = "restic-backups-${name}"; CacheDirectory = "restic-backups-${name}"; CacheDirectoryMode = "0700"; PrivateTmp = true; } // lib.optionalAttrs (backup.environmentFile != null) { } // lib.optionalAttrs (backup.environmentFile != null) { EnvironmentFile = backup.environmentFile; }; } // lib.optionalAttrs (backup.initialize || doBackup || backup.backupPrepareCommand != null) { } // lib.optionalAttrs (backup.initialize || doBackup || backup.backupPrepareCommand != null) { preStart = '' ${lib.optionalString (backup.backupPrepareCommand != null) '' ${pkgs.writeScript "backupPrepareCommand" backup.backupPrepareCommand} Loading @@ -377,7 +429,8 @@ in ${pkgs.writeScript "dynamicFilesFromScript" backup.dynamicFilesFrom} >> ${filesFromTmpFile} ''} ''; } // lib.optionalAttrs (doBackup || backup.backupCleanupCommand != null) { } // lib.optionalAttrs (doBackup || backup.backupCleanupCommand != null) { postStop = '' ${lib.optionalString (backup.backupCleanupCommand != null) '' ${pkgs.writeScript "backupCleanupCommand" backup.backupCleanupCommand} Loading @@ -386,22 +439,25 @@ in rm ${filesFromTmpFile} ''} ''; }) } ) config.services.restic.backups; systemd.timers = lib.mapAttrs' (name: backup: lib.nameValuePair "restic-backups-${name}" { ) config.services.restic.backups; systemd.timers = lib.mapAttrs' ( name: backup: lib.nameValuePair "restic-backups-${name}" { wantedBy = [ "timers.target" ]; timerConfig = backup.timerConfig; }) (lib.filterAttrs (_: backup: backup.timerConfig != null) config.services.restic.backups); } ) (lib.filterAttrs (_: backup: backup.timerConfig != null) config.services.restic.backups); # generate wrapper scripts, as described in the createWrapper option environment.systemPackages = lib.mapAttrsToList (name: backup: let environment.systemPackages = lib.mapAttrsToList ( name: backup: let extraOptions = lib.concatMapStrings (arg: " -o ${arg}") backup.extraOptions; resticCmd = "${backup.package}/bin/restic${extraOptions}"; in pkgs.writeShellScriptBin "restic-${name}" '' in pkgs.writeShellScriptBin "restic-${name}" '' set -a # automatically export variables ${lib.optionalString (backup.environmentFile != null) "source ${backup.environmentFile}"} # set same environment variables as the systemd service Loading @@ -413,6 +469,7 @@ in PATH=${config.systemd.services."restic-backups-${name}".environment.PATH}:$PATH exec ${resticCmd} "$@" '') (lib.filterAttrs (_: v: v.createWrapper) config.services.restic.backups); '' ) (lib.filterAttrs (_: v: v.createWrapper) config.services.restic.backups); }; }