From e67e79642e1091db5e964488696cfae01d787f3d Mon Sep 17 00:00:00 2001 From: Guillaume Girol Date: Sun, 25 Apr 2021 12:00:00 +0000 Subject: [PATCH] nixos/duplicity: add options to not keep backups forever Current module add backups forever, with no way to prune old ones. Add an option to remove backups after n full backups or after some amount of time. Also run duplicity cleanup to clean unused files in case some previous backup was improperly interrupted. --- nixos/modules/services/backup/duplicity.nix | 52 +++++++++++++++------ 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/nixos/modules/services/backup/duplicity.nix b/nixos/modules/services/backup/duplicity.nix index 7bf81a938f1..42d78376d83 100644 --- a/nixos/modules/services/backup/duplicity.nix +++ b/nixos/modules/services/backup/duplicity.nix @@ -91,6 +91,28 @@ in 1. ''; }; + + cleanup = { + maxAge = mkOption { + type = types.nullOr types.str; + default = null; + example = "6M"; + description = '' + If non-null, delete all backup sets older than the given time. Old backup sets + will not be deleted if backup sets newer than time depend on them. + ''; + }; + maxFull = mkOption { + type = types.nullOr types.int; + default = null; + example = 2; + description = '' + If non-null, delete all backups sets that are older than the count:th last full + backup (in other words, keep the last count full backups and + associated incremental sets). + ''; + }; + }; }; config = mkIf cfg.enable { @@ -100,20 +122,24 @@ in environment.HOME = stateDirectory; - serviceConfig = { - ExecStart = '' - ${pkgs.duplicity}/bin/duplicity ${escapeShellArgs ( - [ - cfg.root - cfg.targetUrl - "--archive-dir" - stateDirectory - ] - ++ concatMap (p: [ "--include" p ]) cfg.include - ++ concatMap (p: [ "--exclude" p ]) cfg.exclude - ++ cfg.extraFlags - )} + script = + let + target = escapeShellArg cfg.targetUrl; + extra = escapeShellArgs ([ "--archive-dir" stateDirectory ] ++ cfg.extraFlags); + dup = "${pkgs.duplicity}/bin/duplicity"; + in + '' + set -x + ${dup} cleanup ${target} --force ${extra} + ${lib.optionalString (cfg.cleanup.maxAge != null) "${dup} remove-older-than ${lib.escapeShellArg cfg.cleanup.maxAge} ${target} --force ${extra}"} + ${lib.optionalString (cfg.cleanup.maxFull != null) "${dup} remove-all-but-n-full ${toString cfg.cleanup.maxFull} ${target} --force ${extra}"} + exec ${dup} incr ${lib.escapeShellArgs ( + [ cfg.root cfg.targetUrl ] + ++ concatMap (p: [ "--include" p ]) cfg.include + ++ concatMap (p: [ "--exclude" p ]) cfg.exclude + )} ${extra} ''; + serviceConfig = { PrivateTmp = true; ProtectSystem = "strict"; ProtectHome = "read-only";