{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.tarsnap;
optionalNullStr = e: v: if e == null then "" else v;
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}
${optionalString cfg.aggressiveNetworking "aggressive-networking"}
${concatStringsSep "\n" (map (v: "exclude "+v) cfg.excludes)}
${concatStringsSep "\n" (map (v: "include "+v) cfg.includes)}
${optionalString cfg.lowmem "lowmem"}
${optionalString cfg.verylowmem "verylowmem"}
'';
in
{
options = {
services.tarsnap = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
If enabled, NixOS will periodically create backups of the
specified directories using the tarsnap
backup service. This installs a systemd
service called tarsnap-backup which is
periodically run by cron, or you may run it on-demand.
See the Tarsnap Getting
Started page.
'';
};
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.
'';
};
cachedir = mkOption {
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.
'';
};
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 * * * *";
};
}
'';
description = ''
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.
'';
};
};
};
config = mkIf cfg.enable {
assertions =
(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" ];
path = [ pkgs.tarsnap pkgs.coreutils ];
scriptArgs = "%i";
script = ''
mkdir -p -m 0755 $(dirname ${cfg.cachedir})
mkdir -p -m 0600 ${cfg.cachedir}
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 = 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 ];
};
}