From b0a51de6c10a7a1aba70c0a4aabe33a41bfb4c2c Mon Sep 17 00:00:00 2001 From: "Ricardo M. Correia" Date: Tue, 17 Mar 2015 04:32:58 +0100 Subject: [PATCH 1/2] nixos/zfs: Keep zero-sized auto snapshots by default Otherwise, in certain cases, snapshots of infrequently-modified filesystems can be kept for a much longer time than the user would normally expect, and cause a large amount of extra disk space to be consumed. Also added flag to snapshot filesystems in parallel by default. I've also added a configuration option for zfs-auto-snapshot flags, so that the user can override them. For example, the user may want to append --utc to the list of default options, so that the snapshot names don't cause name conflicts or apparent time reversals due to daylight savings or timezone changes. --- nixos/modules/tasks/filesystems/zfs.nix | 34 ++++++++++++++++++++----- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix index 1ac89c4c255..71054ff238e 100644 --- a/nixos/modules/tasks/filesystems/zfs.nix +++ b/nixos/modules/tasks/filesystems/zfs.nix @@ -12,6 +12,7 @@ let cfgSpl = config.boot.spl; cfgZfs = config.boot.zfs; cfgSnapshots = config.services.zfs.autoSnapshot; + cfgSnapFlags = cfgSnapshots.flags; inInitrd = any (fs: fs == "zfs") config.boot.initrd.supportedFilesystems; inSystem = any (fs: fs == "zfs") config.boot.supportedFilesystems; @@ -137,6 +138,25 @@ in ''; }; + flags = mkOption { + default = "-k -p"; + example = "-k -p --utc"; + type = types.str; + description = '' + Flags to pass to the zfs-auto-snapshot command. + + Run zfs-auto-snapshot (without any arguments) to + see available flags. + + If it's not too inconvenient for snapshots to have timestamps in UTC, + it is suggested that you append --utc to the list + of default options (see example). + + Otherwise, snapshot names can cause name conflicts or apparent time + reversals due to daylight savings, timezone or other date/time changes. + ''; + }; + frequent = mkOption { default = 4; type = types.int; @@ -232,7 +252,9 @@ in environment.etc."zfs/zed.d".source = "${zfsUserPkg}/etc/zfs/zed.d/*"; system.fsPackages = [ zfsUserPkg ]; # XXX: needed? zfs doesn't have (need) a fsck - environment.systemPackages = [ zfsUserPkg ]; + environment.systemPackages = [ zfsUserPkg ] + ++ optional enableAutoSnapshots autosnapPkg; # so the user can run the command to see flags + services.udev.packages = [ zfsUserPkg ]; # to hook zvol naming, etc. systemd.packages = [ zfsUserPkg ]; @@ -289,7 +311,7 @@ in after = [ "zfs-import.target" ]; serviceConfig = { Type = "oneshot"; - ExecStart = "${zfsAutoSnap} frequent ${toString cfgSnapshots.frequent}"; + ExecStart = "${zfsAutoSnap} ${cfgSnapFlags} frequent ${toString cfgSnapshots.frequent}"; }; restartIfChanged = false; startAt = "*:15,30,45"; @@ -300,7 +322,7 @@ in after = [ "zfs-import.target" ]; serviceConfig = { Type = "oneshot"; - ExecStart = "${zfsAutoSnap} hourly ${toString cfgSnapshots.hourly}"; + ExecStart = "${zfsAutoSnap} ${cfgSnapFlags} hourly ${toString cfgSnapshots.hourly}"; }; restartIfChanged = false; startAt = "hourly"; @@ -311,7 +333,7 @@ in after = [ "zfs-import.target" ]; serviceConfig = { Type = "oneshot"; - ExecStart = "${zfsAutoSnap} daily ${toString cfgSnapshots.daily}"; + ExecStart = "${zfsAutoSnap} ${cfgSnapFlags} daily ${toString cfgSnapshots.daily}"; }; restartIfChanged = false; startAt = "daily"; @@ -322,7 +344,7 @@ in after = [ "zfs-import.target" ]; serviceConfig = { Type = "oneshot"; - ExecStart = "${zfsAutoSnap} weekly ${toString cfgSnapshots.weekly}"; + ExecStart = "${zfsAutoSnap} ${cfgSnapFlags} weekly ${toString cfgSnapshots.weekly}"; }; restartIfChanged = false; startAt = "weekly"; @@ -333,7 +355,7 @@ in after = [ "zfs-import.target" ]; serviceConfig = { Type = "oneshot"; - ExecStart = "${zfsAutoSnap} monthly ${toString cfgSnapshots.monthly}"; + ExecStart = "${zfsAutoSnap} ${cfgSnapFlags} monthly ${toString cfgSnapshots.monthly}"; }; restartIfChanged = false; startAt = "monthly"; From 6197fdc02d20763e10beb73c0b6384c255f142b2 Mon Sep 17 00:00:00 2001 From: "Ricardo M. Correia" Date: Fri, 20 Mar 2015 03:49:25 +0100 Subject: [PATCH 2/2] nixos/zfs: Refactor auto-snapshots and make them persistent If you power off your machine frequently, you could miss the execution of some snapshots. This is more troublesome the more infrequently the snapshots are triggered. For example, monthly snapshots only execute at exactly midnight on the first day of the month. If you only have your machine powered on at that time with probability 50%, then half the snapshots won't be triggered. This means that if you wanted to keep 3 monthly snapshots, then instead of keeping 3 months' worth of snapshotted data as you expected, you would end up with snapshots spanning back 6 months. Adding the "Persistent = yes" option to auto-snapshot timer units makes a missed snapshot execute when booting up the machine. --- nixos/modules/tasks/filesystems/zfs.nix | 89 ++++++++++--------------- 1 file changed, 36 insertions(+), 53 deletions(-) diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix index 71054ff238e..a4b01bc2fd1 100644 --- a/nixos/modules/tasks/filesystems/zfs.nix +++ b/nixos/modules/tasks/filesystems/zfs.nix @@ -46,6 +46,8 @@ let dataPools = unique (filter (pool: !(elem pool rootPools)) allPools); + snapshotNames = [ "frequent" "hourly" "daily" "weekly" "monthly" ]; + in { @@ -306,60 +308,41 @@ in }) (mkIf enableAutoSnapshots { - systemd.services."zfs-snapshot-frequent" = { - description = "ZFS auto-snapshotting every 15 mins"; - after = [ "zfs-import.target" ]; - serviceConfig = { - Type = "oneshot"; - ExecStart = "${zfsAutoSnap} ${cfgSnapFlags} frequent ${toString cfgSnapshots.frequent}"; - }; - restartIfChanged = false; - startAt = "*:15,30,45"; - }; + systemd.services = let + descr = name: if name == "frequent" then "15 mins" + else if name == "hourly" then "hour" + else if name == "daily" then "day" + else if name == "weekly" then "week" + else if name == "monthly" then "month" + else throw "unknown snapshot name"; + numSnapshots = name: builtins.getAttr name cfgSnapshots; + in builtins.listToAttrs (map (snapName: + { + name = "zfs-snapshot-${snapName}"; + value = { + description = "ZFS auto-snapshotting every ${descr snapName}"; + after = [ "zfs-import.target" ]; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${zfsAutoSnap} ${cfgSnapFlags} ${snapName} ${toString (numSnapshots snapName)}"; + }; + restartIfChanged = false; + }; + }) snapshotNames); - systemd.services."zfs-snapshot-hourly" = { - description = "ZFS auto-snapshotting every hour"; - after = [ "zfs-import.target" ]; - serviceConfig = { - Type = "oneshot"; - ExecStart = "${zfsAutoSnap} ${cfgSnapFlags} hourly ${toString cfgSnapshots.hourly}"; - }; - restartIfChanged = false; - startAt = "hourly"; - }; - - systemd.services."zfs-snapshot-daily" = { - description = "ZFS auto-snapshotting every day"; - after = [ "zfs-import.target" ]; - serviceConfig = { - Type = "oneshot"; - ExecStart = "${zfsAutoSnap} ${cfgSnapFlags} daily ${toString cfgSnapshots.daily}"; - }; - restartIfChanged = false; - startAt = "daily"; - }; - - systemd.services."zfs-snapshot-weekly" = { - description = "ZFS auto-snapshotting every week"; - after = [ "zfs-import.target" ]; - serviceConfig = { - Type = "oneshot"; - ExecStart = "${zfsAutoSnap} ${cfgSnapFlags} weekly ${toString cfgSnapshots.weekly}"; - }; - restartIfChanged = false; - startAt = "weekly"; - }; - - systemd.services."zfs-snapshot-monthly" = { - description = "ZFS auto-snapshotting every month"; - after = [ "zfs-import.target" ]; - serviceConfig = { - Type = "oneshot"; - ExecStart = "${zfsAutoSnap} ${cfgSnapFlags} monthly ${toString cfgSnapshots.monthly}"; - }; - restartIfChanged = false; - startAt = "monthly"; - }; + systemd.timers = let + timer = name: if name == "frequent" then "*:15,30,45" else name; + in builtins.listToAttrs (map (snapName: + { + name = "zfs-snapshot-${snapName}"; + value = { + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = timer snapName; + Persistent = "yes"; + }; + }; + }) snapshotNames); }) ]; }