flake/modules/nixos/services/backup-client.nix
2025-12-21 19:30:01 +03:00

95 lines
2.8 KiB
Nix

# Backup client - pulls backups from remote servers via rsync
# Supports full backups and selective service backups over Tailscale
{
config,
lib,
pkgs,
...
}:
let
cfg = config.osbmModules.services.backup-client;
# Create a backup job for each configured backup
makeBackupService =
name: backupCfg:
let
# Build rsync paths based on what we're backing up
sourcePaths =
if backupCfg.fullBackup then
[ "/persist" ] # Full backup of everything
else if backupCfg.services != [ ] then
# Selective service backups
map (service: "/persist/var/lib/${service}") backupCfg.services
++ lib.optional (builtins.elem "vaultwarden" backupCfg.services) "/persist/backup/vaultwarden"
else
[ ]; # Empty list if nothing to backup
# Rsync command for each source path
rsyncCommands = map (source: ''
echo "Backing up ${source} from ${backupCfg.remoteHost}..."
${pkgs.rsync}/bin/rsync -avz --delete \
-e "${pkgs.openssh}/bin/ssh -o StrictHostKeyChecking=accept-new" \
${backupCfg.remoteUser}@${backupCfg.remoteHost}:${source}/ \
${backupCfg.localPath}/${builtins.baseNameOf source}/
'') sourcePaths;
in
{
description = "Backup ${name} from ${backupCfg.remoteHost}";
wants = [ "network-online.target" ];
after = [ "network-online.target" ];
serviceConfig = {
Type = "oneshot";
User = "root";
};
script = ''
set -e
# Create backup directory if it doesn't exist
mkdir -p ${backupCfg.localPath}
# Run rsync for each source path
${lib.concatStringsSep "\n" rsyncCommands}
echo "Backup ${name} completed successfully at $(date)"
'';
};
makeBackupTimer = name: backupCfg: {
description = "Timer for ${name} backup";
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = backupCfg.schedule;
Persistent = true;
RandomizedDelaySec = "30m"; # Randomize to avoid all backups running at once
};
};
in
{
config = lib.mkIf cfg.enable {
# Create systemd services for each backup
systemd.services = lib.mapAttrs' (
name: backupCfg: lib.nameValuePair "backup-${name}" (makeBackupService name backupCfg)
) cfg.backups;
# Create systemd timers for each backup
systemd.timers = lib.mapAttrs' (
name: backupCfg: lib.nameValuePair "backup-${name}" (makeBackupTimer name backupCfg)
) cfg.backups;
# Ensure rsync and openssh are available
environment.systemPackages = with pkgs; [
rsync
openssh
];
# Ensure Tailscale is enabled for secure connections
assertions = [
{
assertion = config.services.tailscale.enable;
message = "backup-client requires Tailscale to be enabled for secure connections";
}
];
};
}