Commit b1918bc384cd

Vincent Demeester <vincent@sbr.pm>
2026-05-07 11:02:53
feat: added restic backup for PDS data on carthage
Daily backup of /var/lib/pds to aix via SFTP with ntfy notifications. Retention: 7 daily, 4 weekly, 12 monthly.
1 parent efcb263
Changed files (3)
secrets/carthage/restic-aix-password.age
Binary file
systems/carthage/extra.nix
@@ -347,6 +347,10 @@ in
           command = "/run/current-system/sw/bin/systemd-run";
           options = [ "NOPASSWD" ];
         }
+        {
+          command = "${pkgs.rsync}/bin/rsync -a --delete /var/lib/pds/ /tmp/pds-backup/";
+          options = [ "NOPASSWD" ];
+        }
       ];
     }
   ];
@@ -604,6 +608,76 @@ in
     group = "pds";
   };
 
+  age.secrets."restic-aix-password" = {
+    file = ../../secrets/carthage/restic-aix-password.age;
+    mode = "400";
+    owner = "vincent";
+    group = "users";
+  };
+
+  services.restic.backups.aix-pds = {
+    user = "vincent";
+    repository = "sftp:vincent@aix.sbr.pm:/data/backup/restic/carthage";
+    passwordFile = config.age.secrets."restic-aix-password".path;
+
+    paths = [
+      "/tmp/pds-backup" # Snapshot copy of PDS data
+    ];
+
+    timerConfig = {
+      OnCalendar = "daily";
+      Persistent = true;
+      RandomizedDelaySec = "1h";
+    };
+
+    pruneOpts = [
+      "--keep-daily 7"
+      "--keep-weekly 4"
+      "--keep-monthly 12"
+    ];
+
+    extraBackupArgs = [
+      "--exclude-caches"
+      "--verbose"
+    ];
+
+    checkOpts = [
+      "--read-data-subset=5%"
+    ];
+
+    backupPrepareCommand = ''
+      ${pkgs.coreutils}/bin/rm -rf /tmp/pds-backup
+      ${pkgs.sudo}/bin/sudo ${pkgs.rsync}/bin/rsync -a --delete /var/lib/pds/ /tmp/pds-backup/
+      ${pkgs.curl}/bin/curl \
+        -H "Authorization: Bearer $(${pkgs.coreutils}/bin/tr -d '\n' < ${
+          config.age.secrets."ntfy-token".path
+        })" \
+        -H "Title: Restic Backup Starting (carthage)" \
+        -d "Starting PDS backup to aix" \
+        https://ntfy.sbr.pm/backups
+    '';
+
+    backupCleanupCommand = ''
+      ${pkgs.curl}/bin/curl \
+        -H "Authorization: Bearer $(${pkgs.coreutils}/bin/tr -d '\n' < ${
+          config.age.secrets."ntfy-token".path
+        })" \
+        -H "Title: Restic Backup Complete (carthage)" \
+        -H "Tags: white_check_mark" \
+        -d "PDS backup to aix completed successfully" \
+        https://ntfy.sbr.pm/backups || \
+      ${pkgs.curl}/bin/curl \
+        -H "Authorization: Bearer $(${pkgs.coreutils}/bin/tr -d '\n' < ${
+          config.age.secrets."ntfy-token".path
+        })" \
+        -H "Title: Restic Backup Failed (carthage)" \
+        -H "Tags: x,warning" \
+        -H "Priority: high" \
+        -d "PDS backup to aix failed! Check logs: journalctl -u restic-backups-aix-pds.service" \
+        https://ntfy.sbr.pm/backups
+    '';
+  };
+
   services.bluesky-pds = {
     enable = true;
     pdsadmin.enable = true;
secrets.nix
@@ -44,6 +44,8 @@ in
 {
   # ATProto PDS secrets
   "secrets/carthage/pds.env.age".publicKeys = users ++ [ carthage ];
+  # Restic backup password for carthage
+  "secrets/carthage/restic-aix-password.age".publicKeys = users ++ [ carthage ];
 
   # Mail passwords
   "secrets/mails/icloud-vdemeester.age".publicKeys = users ++ [ athena ];