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 ];
};