at v192 9.6 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.tarsnap; 7 8 configFile = cfg: '' 9 cachedir ${config.services.tarsnap.cachedir} 10 keyfile ${config.services.tarsnap.keyfile} 11 ${optionalString cfg.nodump "nodump"} 12 ${optionalString cfg.printStats "print-stats"} 13 ${optionalString cfg.printStats "humanize-numbers"} 14 ${optionalString (cfg.checkpointBytes != null) ("checkpoint-bytes "+cfg.checkpointBytes)} 15 ${optionalString cfg.aggressiveNetworking "aggressive-networking"} 16 ${concatStringsSep "\n" (map (v: "exclude "+v) cfg.excludes)} 17 ${concatStringsSep "\n" (map (v: "include "+v) cfg.includes)} 18 ${optionalString cfg.lowmem "lowmem"} 19 ${optionalString cfg.verylowmem "verylowmem"} 20 ${optionalString (cfg.maxbw != null) ("maxbw "+toString cfg.maxbw)} 21 ${optionalString (cfg.maxbwRateUp != null) ("maxbw-rate-up "+toString cfg.maxbwRateUp)} 22 ${optionalString (cfg.maxbwRateDown != null) ("maxbw-rate-down "+toString cfg.maxbwRateDown)} 23 ''; 24in 25{ 26 options = { 27 services.tarsnap = { 28 enable = mkOption { 29 type = types.bool; 30 default = false; 31 description = '' 32 Enable periodic tarsnap backups. 33 ''; 34 }; 35 36 keyfile = mkOption { 37 type = types.str; 38 default = "/root/tarsnap.key"; 39 description = '' 40 The keyfile which associates this machine with your tarsnap 41 account. 42 Create the keyfile with <command>tarsnap-keygen</command>. 43 44 The keyfile name should be given as a string and not a path, to 45 avoid the key being copied into the Nix store. 46 ''; 47 }; 48 49 cachedir = mkOption { 50 type = types.nullOr types.path; 51 default = "/var/cache/tarsnap"; 52 description = '' 53 The cache allows tarsnap to identify previously stored data 54 blocks, reducing archival time and bandwidth usage. 55 56 Should the cache become desynchronized or corrupted, tarsnap 57 will refuse to run until you manually rebuild the cache with 58 <command>tarsnap --fsck</command>. 59 60 Set to <literal>null</literal> to disable caching. 61 ''; 62 }; 63 64 archives = mkOption { 65 type = types.attrsOf (types.submodule ( 66 { 67 options = { 68 nodump = mkOption { 69 type = types.bool; 70 default = true; 71 description = '' 72 Exclude files with the <literal>nodump</literal> flag. 73 ''; 74 }; 75 76 printStats = mkOption { 77 type = types.bool; 78 default = true; 79 description = '' 80 Print global archive statistics upon completion. 81 The output is available via 82 <command>systemctl status tarsnap@archive-name</command>. 83 ''; 84 }; 85 86 checkpointBytes = mkOption { 87 type = types.nullOr types.str; 88 default = "1GB"; 89 description = '' 90 Create a checkpoint every <literal>checkpointBytes</literal> 91 of uploaded data (optionally specified using an SI prefix). 92 93 1GB is the minimum value. A higher value is recommended, 94 as checkpointing is expensive. 95 96 Set to <literal>null</literal> to disable checkpointing. 97 ''; 98 }; 99 100 period = mkOption { 101 type = types.str; 102 default = "01:15"; 103 example = "hourly"; 104 description = '' 105 Create archive at this interval. 106 107 The format is described in 108 <citerefentry><refentrytitle>systemd.time</refentrytitle> 109 <manvolnum>7</manvolnum></citerefentry>. 110 ''; 111 }; 112 113 aggressiveNetworking = mkOption { 114 type = types.bool; 115 default = false; 116 description = '' 117 Upload data over multiple TCP connections, potentially 118 increasing tarsnap's bandwidth utilisation at the cost 119 of slowing down all other network traffic. Not 120 recommended unless TCP congestion is the dominant 121 limiting factor. 122 ''; 123 }; 124 125 directories = mkOption { 126 type = types.listOf types.path; 127 default = []; 128 description = "List of filesystem paths to archive."; 129 }; 130 131 excludes = mkOption { 132 type = types.listOf types.str; 133 default = []; 134 description = '' 135 Exclude files and directories matching these patterns. 136 ''; 137 }; 138 139 includes = mkOption { 140 type = types.listOf types.str; 141 default = []; 142 description = '' 143 Include only files and directories matching these 144 patterns (the empty list includes everything). 145 146 Exclusions have precedence over inclusions. 147 ''; 148 }; 149 150 lowmem = mkOption { 151 type = types.bool; 152 default = false; 153 description = '' 154 Reduce memory consumption by not caching small files. 155 Possibly beneficial if the average file size is smaller 156 than 1 MB and the number of files is lower than the 157 total amount of RAM in KB. 158 ''; 159 }; 160 161 verylowmem = mkOption { 162 type = types.bool; 163 default = false; 164 description = '' 165 Reduce memory consumption by a factor of 2 beyond what 166 <literal>lowmem</literal> does, at the cost of significantly 167 slowing down the archiving process. 168 ''; 169 }; 170 171 maxbw = mkOption { 172 type = types.nullOr types.int; 173 default = null; 174 description = '' 175 Abort archival if upstream bandwidth usage in bytes 176 exceeds this threshold. 177 ''; 178 }; 179 180 maxbwRateUp = mkOption { 181 type = types.nullOr types.int; 182 default = null; 183 example = literalExample "25 * 1000"; 184 description = '' 185 Upload bandwidth rate limit in bytes. 186 ''; 187 }; 188 189 maxbwRateDown = mkOption { 190 type = types.nullOr types.int; 191 default = null; 192 example = literalExample "50 * 1000"; 193 description = '' 194 Download bandwidth rate limit in bytes. 195 ''; 196 }; 197 }; 198 } 199 )); 200 201 default = {}; 202 203 example = literalExample '' 204 { 205 nixos = 206 { directories = [ "/home" "/root/ssl" ]; 207 }; 208 209 gamedata = 210 { directories = [ "/var/lib/minecraft "]; 211 period = "*:30"; 212 }; 213 } 214 ''; 215 216 description = '' 217 Tarsnap archive configurations. Each attribute names an archive 218 to be created at a given time interval, according to the options 219 associated with it. When uploading to the tarsnap server, 220 archive names are suffixed by a 1 second resolution timestamp. 221 222 For each member of the set is created a timer which triggers the 223 instanced <literal>tarsnap@</literal> service unit. You may use 224 <command>systemctl start tarsnap@archive-name</command> to 225 manually trigger creation of <literal>archive-name</literal> at 226 any time. 227 ''; 228 }; 229 }; 230 }; 231 232 config = mkIf cfg.enable { 233 assertions = 234 (mapAttrsToList (name: cfg: 235 { assertion = cfg.directories != []; 236 message = "Must specify paths for tarsnap to back up"; 237 }) cfg.archives) ++ 238 (mapAttrsToList (name: cfg: 239 { assertion = !(cfg.lowmem && cfg.verylowmem); 240 message = "You cannot set both lowmem and verylowmem"; 241 }) cfg.archives); 242 243 systemd.services."tarsnap@" = { 244 description = "Tarsnap archive '%i'"; 245 requires = [ "network.target" ]; 246 247 path = [ pkgs.tarsnap pkgs.coreutils ]; 248 scriptArgs = "%i"; 249 script = '' 250 mkdir -p -m 0755 ${dirOf cfg.cachedir} 251 mkdir -p -m 0700 ${cfg.cachedir} 252 chown root:root ${cfg.cachedir} 253 chmod 0700 ${cfg.cachedir} 254 DIRS=`cat /etc/tarsnap/$1.dirs` 255 exec tarsnap --configfile /etc/tarsnap/$1.conf -c -f $1-$(date +"%Y%m%d%H%M%S") $DIRS 256 ''; 257 258 serviceConfig = { 259 IOSchedulingClass = "idle"; 260 NoNewPrivileges = "true"; 261 CapabilityBoundingSet = "CAP_DAC_READ_SEARCH"; 262 }; 263 }; 264 265 systemd.timers = mapAttrs' (name: cfg: nameValuePair "tarsnap@${name}" 266 { timerConfig.OnCalendar = cfg.period; 267 wantedBy = [ "timers.target" ]; 268 }) cfg.archives; 269 270 environment.etc = 271 (mapAttrs' (name: cfg: nameValuePair "tarsnap/${name}.conf" 272 { text = configFile cfg; 273 }) cfg.archives) // 274 (mapAttrs' (name: cfg: nameValuePair "tarsnap/${name}.dirs" 275 { text = concatStringsSep " " cfg.directories; 276 }) cfg.archives); 277 278 environment.systemPackages = [ pkgs.tarsnap ]; 279 }; 280}