diff --git a/nixos/backups.nix b/nixos/backups.nix
index 57cbd0a4..b8423c70 100644
--- a/nixos/backups.nix
+++ b/nixos/backups.nix
@@ -9,8 +9,6 @@ let
];
in {
- disabledModules = [ "services/backup/restic.nix" ];
- imports = [ services/backup/restic.nix ];
services = {
restic.backups.home-to-bolty = {
passwordFile = "/etc/nixos/secrets/restic-password-bolty";
@@ -18,8 +16,6 @@ in {
repository = "rest:http://bolty:8000/";
timerConfig = { OnCalendar = "hourly"; };
extraBackupArgs = extraArgs;
- niceness = 19;
- ioSchedulingClass = "idle";
};
restic.backups.home-to-b2 = {
@@ -29,8 +25,15 @@ in {
timerConfig = { OnCalendar = "hourly"; };
extraBackupArgs = extraArgs;
environmentFile = "/etc/nixos/secrets/b2-env";
- niceness = 19;
- ioSchedulingClass = "idle";
};
};
+
+ systemd.services.restic-backups-home-to-bolty.serviceConfig = {
+ Nice = 19;
+ IOSchedulingClass = "idle";
+ };
+ systemd.services.restic-backups-home-to-b2.serviceConfig = {
+ Nice = 19;
+ IOSchedulingClass = "idle";
+ };
}
diff --git a/nixos/services/backup/restic.nix b/nixos/services/backup/restic.nix
deleted file mode 100644
index e55161eb..00000000
--- a/nixos/services/backup/restic.nix
+++ /dev/null
@@ -1,352 +0,0 @@
-{ config, lib, pkgs, utils, ... }:
-
-with lib;
-
-let
- # Type for a valid systemd unit option. Needed for correctly passing "timerConfig" to "systemd.timers"
- inherit (utils.systemdUtils.unitOptions) unitOption;
-in {
- options.services.restic.backups = mkOption {
- description = ''
- Periodic backups to create with Restic.
- '';
- type = types.attrsOf (types.submodule ({ config, name, ... }: {
- options = {
- passwordFile = mkOption {
- type = types.str;
- description = ''
- Read the repository password from a file.
- '';
- example = "/etc/nixos/restic-password";
- };
-
- environmentFile = mkOption {
- type = with types; nullOr str;
- # added on 2021-08-28, s3CredentialsFile should
- # be removed in the future (+ remember the warning)
- default = config.s3CredentialsFile;
- description = ''
- file containing the credentials to access the repository, in the
- format of an EnvironmentFile as described by systemd.exec(5)
- '';
- };
-
- s3CredentialsFile = mkOption {
- type = with types; nullOr str;
- default = null;
- description = ''
- file containing the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
- for an S3-hosted repository, in the format of an EnvironmentFile
- as described by systemd.exec(5)
- '';
- };
-
- rcloneOptions = mkOption {
- type = with types; nullOr (attrsOf (oneOf [ str bool ]));
- default = null;
- description = ''
- Options to pass to rclone to control its behavior.
- See for
- available options. When specifying option names, strip the
- leading --. To set a flag such as
- --drive-use-trash, which does not take a value,
- set the value to the Boolean true.
- '';
- example = {
- bwlimit = "10M";
- drive-use-trash = "true";
- };
- };
-
- rcloneConfig = mkOption {
- type = with types; nullOr (attrsOf (oneOf [ str bool ]));
- default = null;
- description = ''
- Configuration for the rclone remote being used for backup.
- See the remote's specific options under rclone's docs at
- . When specifying
- option names, use the "config" name specified in the docs.
- For example, to set --b2-hard-delete for a B2
- remote, use hard_delete = true in the
- attribute set.
- Warning: Secrets set in here will be world-readable in the Nix
- store! Consider using the rcloneConfigFile
- option instead to specify secret values separately. Note that
- options set here will override those set in the config file.
- '';
- example = {
- type = "b2";
- account = "xxx";
- key = "xxx";
- hard_delete = true;
- };
- };
-
- rcloneConfigFile = mkOption {
- type = with types; nullOr path;
- default = null;
- description = ''
- Path to the file containing rclone configuration. This file
- must contain configuration for the remote specified in this backup
- set and also must be readable by root. Options set in
- rcloneConfig will override those set in this
- file.
- '';
- };
-
- repository = mkOption {
- type = with types; nullOr str;
- default = null;
- description = ''
- repository to backup to.
- '';
- example = "sftp:backup@192.168.1.100:/backups/${name}";
- };
-
- repositoryFile = mkOption {
- type = with types; nullOr path;
- default = null;
- description = ''
- Path to the file containing the repository location to backup to.
- '';
- };
-
- paths = mkOption {
- type = types.nullOr (types.listOf types.str);
- default = null;
- description = ''
- Which paths to backup. If null or an empty array, no
- backup command will be run. This can be used to create a
- prune-only job.
- '';
- example = [ "/var/lib/postgresql" "/home/user/backup" ];
- };
-
- timerConfig = mkOption {
- type = types.attrsOf unitOption;
- 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.
- '';
- };
-
- pruneOpts = mkOption {
- type = types.listOf types.str;
- default = [ ];
- description = ''
- A list of options (--keep-* et al.) for 'restic forget
- --prune', to automatically prune old snapshots. The
- 'forget' command is run *after* the 'backup' command, so
- keep that in mind when constructing the --keep-* options.
- '';
- example = [
- "--keep-daily 7"
- "--keep-weekly 5"
- "--keep-monthly 12"
- "--keep-yearly 75"
- ];
- };
-
- dynamicFilesFrom = mkOption {
- type = with types; nullOr str;
- default = null;
- description = ''
- A script that produces a list of files to back up. The
- results of this command are given to the '--files-from'
- option.
- '';
- example = "find /home/matt/git -type d -name .git";
- };
-
- backupPrepareCommand = mkOption {
- type = with types; nullOr str;
- default = null;
- description = ''
- A script that must run before starting the backup process.
- '';
- };
-
- backupCleanupCommand = mkOption {
- type = with types; nullOr str;
- default = null;
- description = ''
- A script that must run after finishing the backup process.
- '';
- };
- niceness = mkOption {
- description =
- "Niceness for local instances of btrbk. Also applies to remote ones connecting via ssh when positive.";
- type = types.ints.between (-20) 19;
- default = 10;
- };
- ioSchedulingClass = mkOption {
- description =
- "IO scheduling class for btrbk (see ionice(1) for a quick description). Applies to local instances, and remote ones connecting by ssh if set to idle.";
- type = types.enum [ "idle" "best-effort" "realtime" ];
- default = "best-effort";
- };
- };
- }));
- 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 = {
- warnings = mapAttrsToList (n: v:
- "services.restic.backups.${n}.s3CredentialsFile is deprecated, please use services.restic.backups.${n}.environmentFile instead.")
- (filterAttrs (n: v: v.s3CredentialsFile != null)
- config.services.restic.backups);
- systemd.services = mapAttrs' (name: backup:
- let
- extraOptions = concatMapStrings (arg: " -o ${arg}") backup.extraOptions;
- resticCmd = "${pkgs.restic}/bin/restic${extraOptions}";
- filesFromTmpFile = "/run/restic-backups-${name}/includes";
- backupPaths = if (backup.dynamicFilesFrom == null) then
- if (backup.paths != null) then
- concatStringsSep " " backup.paths
- else
- ""
- else
- "--files-from ${filesFromTmpFile}";
- pruneCmd = optionals (builtins.length backup.pruneOpts > 0) [
- (resticCmd + " forget --prune "
- + (concatStringsSep " " backup.pruneOpts))
- (resticCmd + " check")
- ];
- # Helper functions for rclone remotes
- rcloneRemoteName =
- builtins.elemAt (splitString ":" backup.repository) 1;
- rcloneAttrToOpt = v:
- "RCLONE_" + toUpper (builtins.replaceStrings [ "-" ] [ "_" ] v);
- rcloneAttrToConf = v:
- "RCLONE_CONFIG_" + toUpper (rcloneRemoteName + "_" + v);
- toRcloneVal = v: if lib.isBool v then lib.boolToString v else v;
- in nameValuePair "restic-backups-${name}" ({
- environment = {
- RESTIC_PASSWORD_FILE = backup.passwordFile;
- RESTIC_REPOSITORY = backup.repository;
- RESTIC_REPOSITORY_FILE = backup.repositoryFile;
- } // optionalAttrs (backup.rcloneOptions != null) (mapAttrs'
- (name: value:
- nameValuePair (rcloneAttrToOpt name) (toRcloneVal value))
- backup.rcloneOptions)
- // optionalAttrs (backup.rcloneConfigFile != null) {
- RCLONE_CONFIG = backup.rcloneConfigFile;
- } // optionalAttrs (backup.rcloneConfig != null) (mapAttrs'
- (name: value:
- nameValuePair (rcloneAttrToConf name) (toRcloneVal value))
- backup.rcloneConfig);
- path = [ pkgs.openssh ];
- restartIfChanged = false;
- serviceConfig = {
- Type = "oneshot";
- ExecStart = (optionals (backupPaths != "") [
- "${resticCmd} backup --cache-dir=%C/restic-backups-${name} ${
- concatStringsSep " " backup.extraBackupArgs
- } ${backupPaths}"
- ]) ++ pruneCmd;
- User = backup.user;
- RuntimeDirectory = "restic-backups-${name}";
- CacheDirectory = "restic-backups-${name}";
- CacheDirectoryMode = "0700";
- Nice = backup.niceness;
- IOSchedulingClass = backup.ioSchedulingClass;
- } // optionalAttrs (backup.environmentFile != null) {
- EnvironmentFile = backup.environmentFile;
- };
- } // optionalAttrs (backup.initialize || backup.dynamicFilesFrom != null
- || backup.backupPrepareCommand != null) {
- preStart = ''
- ${optionalString (backup.backupPrepareCommand != null) ''
- ${pkgs.writeScript "backupPrepareCommand"
- backup.backupPrepareCommand}
- ''}
- ${optionalString (backup.initialize) ''
- ${resticCmd} snapshots || ${resticCmd} init
- ''}
- ${optionalString (backup.dynamicFilesFrom != null) ''
- ${
- pkgs.writeScript "dynamicFilesFromScript"
- backup.dynamicFilesFrom
- } > ${filesFromTmpFile}
- ''}
- '';
- } // optionalAttrs (backup.dynamicFilesFrom != null
- || backup.backupCleanupCommand != null) {
- postStart = ''
- ${optionalString (backup.backupCleanupCommand != null) ''
- ${pkgs.writeScript "backupCleanupCommand"
- backup.backupCleanupCommand}
- ''}
- ${optionalString (backup.dynamicFilesFrom != null) ''
- rm ${filesFromTmpFile}
- ''}
- '';
- })) config.services.restic.backups;
- systemd.timers = mapAttrs' (name: backup:
- nameValuePair "restic-backups-${name}" {
- wantedBy = [ "timers.target" ];
- timerConfig = backup.timerConfig;
- }) config.services.restic.backups;
- };
-}