{ pkgs, lib, hostName, ... }: with builtins; { # syncoid --create-bookmark --no-privilege-elevation --no-sync-snap --recvoptions '' --sendoptions raw --recursive oignon/home off2/julm/backup/oignon/home # zfs list -t snapshot -o name | grep ^oignon/home | while read -r snap; do zfs bookmark "$snap" "${snap//@/#}"; done # Take regular snapshots, and prune old ones services.sanoid = { enable = true; extraArgs = [ "--verbose" ]; datasets = { "${hostName}/home" = { autosnap = true; autoprune = true; hourly = 12; daily = 3; monthly = 0; yearly = 0; recursive = true; }; "${hostName}/var" = { autosnap = true; autoprune = true; hourly = 12; daily = 1; monthly = 0; yearly = 0; recursive = true; }; "off2/julm/backup/oignon" = { autosnap = false; autoprune = true; hourly = 0; daily = 7; monthly = 3; yearly = 0; recursive = true; }; }; }; # Trigger backups when disks are plugged services.udev.extraRules = '' ACTION=="add", SUBSYSTEM=="block", KERNEL=="sd*", ENV{ID_SERIAL}=="WDC_WD10JPVT-22A1YT0_WD-WX21AC2F3987", ENV{SYSTEMD_WANTS}+="zfs-local-backup-home@WD10JPVT.service", ENV{SYSTEMD_ALIAS}="/sys/subsystem/usb/WD10JPVT" # See https://github.com/systemd/systemd/issues/7587#issuecomment-381428545 ACTION=="remove", SUBSYSTEM=="block", KERNEL=="sd*", ENV{ID_SERIAL}=="WDC_WD10JPVT-22A1YT0_WD-WX21AC2F3987", TAG+="systemd" ''; # Show what's happening to the user systemd.services."zfs-term@" = { description = "ZFS terminal for: %I"; unitConfig.StopWhenUnneeded = false; environment.DISPLAY = ":0"; environment.XAUTHORITY = "/home/julm/.Xauthority"; after = [ "graphical.target" ]; bindsTo = [ "sys-subsystem-usb-%i.device" ]; serviceConfig = { Type = "simple"; PrivateTmp = true; ExecStart = pkgs.writeShellScript "zfs-force-import" '' DESTPOOL=$1 set -eux ${pkgs.xterm}/bin/xterm -fg white -bg black -fa Monospace -fs 6 \ -title "ZFS backup to: $DESTPOOL" -e "journalctl -f -o short \ -u zfs-force-import@$DESTPOOL \ -u zfs-local-backup-home@$DESTPOOL" '' + " %I"; }; }; # Force zpool import, even if the disk has not been exported, or has been imported on another computer systemd.services."zfs-force-import@" = { description = "ZFS force import: %I"; unitConfig = { StartLimitBurst = 5; StartLimitInterval = 200; StopWhenUnneeded = true; }; wants = [ "zfs-term@%i.service" ]; bindsTo = [ "sys-subsystem-usb-%i.device" ]; path = lib.mkBefore [ "/run/booted-system/sw" ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; PrivateTmp = true; SyslogIdentifier = "zfs-force-import@%i"; Restart = "on-failure"; ExecStart = pkgs.writeShellScript "zfs-force-import" '' DESTPOOL=$1 set -eux # Import the zpool, using stable paths zpool import -d /dev/disk/by-id/ || true zpool import -lFd /dev/disk/by-id/ "$DESTPOOL" || zpool reopen "$DESTPOOL" || zpool import -f -d /dev/disk/by-id/ "$DESTPOOL" || zpool clear -nFX "$DESTPOOL" '' + " %I"; }; }; # Prune old snapshots on the backup and send new ones systemd.services."zfs-local-backup-home@" = { description = "ZFS backup home, on: %I"; wants = [ "zfs-term@%i.service" ]; after = [ "zfs-force-import@%i.service" ]; requires = [ "zfs-force-import@%i.service" ]; bindsTo = [ "sys-subsystem-usb-%i.device" ]; path = lib.mkBefore [ "/run/booted-system/sw" ]; serviceConfig = rec { Type = "oneshot"; PrivateTmp = true; CacheDirectory = [ "zfs-usb-backup/%I" ]; RuntimeDirectory = [ "zfs-usb-backup/%I" ]; User = "julm"; Group = "users"; SyslogIdentifier = "zfs-local-backup-home@%i"; ExecStartPre = "+" + pkgs.writeShellScript "zfs-local-backup-home-startPre" '' DESTPOOL=$1 set -eux if zpool status "$DESTPOOL"; then zfs allow ${User} bookmark,hold,mount,send ${hostName}/home zfs allow ${User} bookmark,create,destroy,load-key,mount,mountpoint,receive,rollback,snapshot "$DESTPOOL"/${User} zpool scrub -p "$DESTPOOL" || true fi '' + " %I"; ExecStart = pkgs.writeShellScript "zfs-local-backup-home" '' set -eu DESTPOOL=$1 # sanoid is quite conservative: # by setting hourly=24, a snapshot must be >24 hours old # and there must been >24 total hourly snapshots, # or nothing is pruned. install -D -m 400 /dev/stdin /tmp/sanoid/sanoid.conf </dev/null || sudo zpool import -d /dev/disk/by-id/ "$zpool" trap "sudo zpool export $zpool" EXIT zfs list -rH -t filesystem -o mounted,mountpoint,name "$zpool"/"$USER"/backup | grep "^no\\s*/" | cut -f 3 | xargs -ortL1 sudo zfs mount -Olv || true ${pkgs.mate.caja-with-extensions}/bin/caja --browser /mnt/"$zpool"/"$USER"/backup ) } ''; programs.bash.shellAliases = { mount-backup-WD10JPVT = "mount-zfs-backup WD10JPVT"; }; }