diff --git a/nixos/modules/services/backup/tarsnap.nix b/nixos/modules/services/backup/tarsnap.nix index 1966242e3dc..1b0bcadca15 100644 --- a/nixos/modules/services/backup/tarsnap.nix +++ b/nixos/modules/services/backup/tarsnap.nix @@ -7,9 +7,9 @@ let optionalNullStr = e: v: if e == null then "" else v; - configFile = pkgs.writeText "tarsnap.conf" '' - cachedir ${cfg.cachedir} - keyfile ${cfg.keyfile} + configFile = cfg: '' + cachedir ${config.services.tarsnap.cachedir} + keyfile ${config.services.tarsnap.keyfile} ${optionalString cfg.nodump "nodump"} ${optionalString cfg.printStats "print-stats"} ${optionalNullStr cfg.checkpointBytes "checkpoint-bytes "+cfg.checkpointBytes} @@ -39,15 +39,15 @@ in ''; }; - label = mkOption { - type = types.str; - default = "nixos"; + keyfile = mkOption { + type = types.path; + default = "/root/tarsnap.key"; description = '' - Specifies the label for archives created by Tarsnap. The - full name will be - label-$(date+"%Y%m%d%H%M%S"). For - example, by default your backups will look similar to - nixos-20140301011501. + Path to the keyfile which identifies the machine + associated with your Tarsnap account. This file can + be created using the + tarsnap-keygen utility, and + providing your Tarsnap login credentials. ''; }; @@ -55,122 +55,158 @@ in type = types.path; default = "/var/cache/tarsnap"; description = '' - Tarsnap operations use a "cache directory" which allows - Tarsnap to identify which blocks of data have been - previously stored; this directory is specified via the - cachedir option. If the cache directory - is lost or out of date, tarsnap creation/deletion operations - will exit with an error message instructing you to run - tarsnap --fsck to regenerate the cache - directory. + Tarsnap operations use a "cache directory" which + allows Tarsnap to identify which blocks of data have + been previously stored; this directory is specified + via the cachedir option. If the + cache directory is lost or out of date, tarsnap + creation/deletion operations will exit with an error + message instructing you to run tarsnap + --fsck to regenerate the cache directory. ''; }; - keyfile = mkOption { - type = types.path; - default = "/root/tarsnap.key"; - description = '' - Path to the keyfile which identifies the machine associated - with your Tarsnap account. This file can be created using - the tarsnap-keygen utility, and providing - your Tarsnap login credentials. + config = mkOption { + type = types.attrsOf (types.submodule ( + { + options = { + nodump = mkOption { + type = types.bool; + default = true; + description = '' + If set to true, then don't + archive files which have the + nodump flag set. + ''; + }; + + printStats = mkOption { + type = types.bool; + default = true; + description = "Print statistics when creating archives."; + }; + + checkpointBytes = mkOption { + type = types.nullOr types.str; + default = "1G"; + description = '' + Create a checkpoint per a particular amount of + uploaded data. By default, Tarsnap will create + checkpoints once per GB of data uploaded. At + minimum, checkpointBytes must be + 1GB. + + Can also be set to null to + disable checkpointing. + ''; + }; + + period = mkOption { + type = types.str; + default = "15 01 * * *"; + description = '' + This option defines (in the format used by cron) + when tarsnap is run for backups. The default is to + backup the specified paths at 01:15 at night every + day. + ''; + }; + + aggressiveNetworking = mkOption { + type = types.bool; + default = false; + description = '' + Aggressive network behaviour: Use multiple TCP + connections when writing archives. Use of this + option is recommended only in cases where TCP + congestion control is known to be the limiting + factor in upload performance. + ''; + }; + + directories = mkOption { + type = types.listOf types.path; + default = []; + description = "List of filesystem paths to archive."; + }; + + excludes = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Exclude files and directories matching the specified + patterns. + ''; + }; + + includes = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Include only files and directories matching the + specified patterns. + + Note that exclusions specified via + excludes take precedence over + inclusions. + ''; + }; + + lowmem = mkOption { + type = types.bool; + default = false; + description = '' + Attempt to reduce tarsnap memory consumption. This + option will slow down the process of creating + archives, but may help on systems where the average + size of files being backed up is less than 1 MB. + ''; + }; + + verylowmem = mkOption { + type = types.bool; + default = false; + description = '' + Try even harder to reduce tarsnap memory + consumption. This can significantly slow down + tarsnap, but reduces its memory usage by an + additional factor of 2 beyond what the + lowmem option does. + ''; + }; + }; + } + )); + + default = {}; + + example = literalExample '' + { + nixos = + { directories = [ "/home" "/root/ssl" ]; + }; + + gamedata = + { directories = [ "/var/lib/minecraft "]; + period = "*/30 * * * *"; + }; + } ''; - }; - nodump = mkOption { - type = types.bool; - default = true; description = '' - If set to true, then don't archive files - which have the nodump flag set. - ''; - }; - - printStats = mkOption { - type = types.bool; - default = true; - description = "Print statistics when creating archives."; - }; - - checkpointBytes = mkOption { - type = types.nullOr types.str; - default = "1G"; - description = '' - Create a checkpoint per a particular amount of uploaded - data. By default, Tarsnap will create checkpoints once per - GB of data uploaded. At minimum, - checkpointBytes must be 1GB. - - Can also be set to null to disable - checkpointing. - ''; - }; - - period = mkOption { - type = types.str; - default = "15 01 * * *"; - description = '' - This option defines (in the format used by cron) when - tarsnap is run for backups. The default is to backup the - specified paths at 01:15 at night every day. - ''; - }; - - aggressiveNetworking = mkOption { - type = types.bool; - default = false; - description = '' - Aggressive network behaviour: Use multiple TCP connections - when writing archives. Use of this option is recommended - only in cases where TCP congestion control is known to be - the limiting factor in upload performance. - ''; - }; - - directories = mkOption { - type = types.listOf types.path; - default = []; - description = "List of filesystem paths to archive."; - }; - - excludes = mkOption { - type = types.listOf types.str; - default = []; - description = '' - Exclude files and directories matching the specified patterns. - ''; - }; - - includes = mkOption { - type = types.listOf types.str; - default = []; - description = '' - Include only files and directories matching the specified patterns. - - Note that exclusions specified via - excludes take precedence over inclusions. - ''; - }; - - lowmem = mkOption { - type = types.bool; - default = false; - description = '' - Attempt to reduce tarsnap memory consumption. This option - will slow down the process of creating archives, but may - help on systems where the average size of files being backed - up is less than 1 MB. - ''; - }; - - verylowmem = mkOption { - type = types.bool; - default = false; - description = '' - Try even harder to reduce tarsnap memory consumption. This - can significantly slow down tarsnap, but reduces its memory - usage by an additional factor of 2 beyond what the - lowmem option does. + Configuration of a Tarsnap archive. In the example, your + machine will have two tarsnap archives: + gamedata (backed up every 30 minutes) and + nixos (backed up at 1:15 AM every night by + default). You can control individual archive backups using + systemctl, using the + tarsnap@nixos or + tarsnap@gamedata units. For example, + systemctl start tarsnap@nixos will + immediately create a new NixOS archive. By default, archives + are suffixed with the timestamp of when they were started, + down to second resolution. This means you can use GNU + sort to sort output easily. ''; }; }; @@ -178,26 +214,40 @@ in config = mkIf cfg.enable { assertions = - [ { assertion = cfg.directories != []; + (mapAttrsToList (name: cfg: + { assertion = cfg.directories != []; message = "Must specify directories for Tarsnap to back up"; - } + }) cfg.config) ++ + (mapAttrsToList (name: cfg: { assertion = cfg.lowmem -> !cfg.verylowmem && (cfg.verylowmem -> !cfg.lowmem); message = "You cannot set both lowmem and verylowmem"; - } - ]; + }) cfg.config); + + systemd.services."tarsnap@" = { + description = "Tarsnap Backup of '%i'"; + requires = [ "network.target" ]; - systemd.services.tarsnap-backup = { - description = "Tarsnap Backup process"; path = [ pkgs.tarsnap pkgs.coreutils ]; + scriptArgs = "%i"; script = '' mkdir -p -m 0755 $(dirname ${cfg.cachedir}) mkdir -p -m 0600 ${cfg.cachedir} - exec tarsnap --configfile ${configFile} -c -f ${cfg.label}-$(date +"%Y%m%d%H%M%S") ${concatStringsSep " " cfg.directories} + DIRS=`cat /etc/tarsnap/$1.dirs` + exec tarsnap --configfile /etc/tarsnap/$1.conf -c -f $1-$(date +"%Y%m%d%H%M%S") $DIRS ''; }; - services.cron.systemCronJobs = optional cfg.enable - "${cfg.period} root ${config.systemd.package}/bin/systemctl start tarsnap-backup.service"; + services.cron.systemCronJobs = mapAttrsToList (name: cfg: + "${cfg.period} root ${config.systemd.package}/bin/systemctl start tarsnap@${name}" + ) cfg.config; + + environment.etc = + (mapAttrs' (name: cfg: nameValuePair "tarsnap/${name}.conf" + { text = configFile cfg; + }) cfg.config) // + (mapAttrs' (name: cfg: nameValuePair "tarsnap/${name}.dirs" + { text = concatStringsSep " " cfg.directories; + }) cfg.config); environment.systemPackages = [ pkgs.tarsnap ]; };