• Introduces a unified backup service generator with notification and stats extraction • Consolidates Borg backup logic, replacing duplicate service definitions • Updates SMTP configuration and Apprise URL generation in secret specifications • Refines file exclusion lists for snapraid
180 lines
5.1 KiB
Nix
180 lines
5.1 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
|
|
let
|
|
# Common repositories
|
|
dockerStorageRepo = "/pool/Backups/DockerStorage";
|
|
forgejoRepo = "/pool/Backups/forgejo";
|
|
|
|
# Shared environment setup
|
|
borgCommonSettings = ''
|
|
# Don't use cache to avoid issues with concurrent backups
|
|
export BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=yes
|
|
export BORG_NON_INTERACTIVE=yes
|
|
'';
|
|
|
|
# Common packages needed for backups
|
|
commonBorgPath = with pkgs; [
|
|
borgbackup
|
|
coreutils
|
|
apprise
|
|
gnugrep
|
|
hostname
|
|
util-linux
|
|
gawk
|
|
];
|
|
|
|
# Repository initialization
|
|
initRepo = repo: ''
|
|
if [ ! -d "${repo}" ]; then
|
|
mkdir -p "${repo}"
|
|
${pkgs.borgbackup}/bin/borg init --encryption=none "${repo}"
|
|
fi
|
|
'';
|
|
|
|
# Notification system
|
|
apprise-url = config.secretsSpec.users.admin.smtp.notifyUrl;
|
|
sendNotification = title: message: ''
|
|
${pkgs.apprise}/bin/apprise -t "${title}" -b "${message}" "${apprise-url}" || true
|
|
'';
|
|
|
|
# Statistics generation
|
|
extractBorgStats = logFile: repoPath: ''
|
|
{
|
|
echo -e "\n==== BACKUP SUMMARY ====\n"
|
|
grep -A10 "Archive name:" ${logFile} || echo "No archive stats found"
|
|
echo -e "\n=== Compression ===\n"
|
|
grep "Compressed size:" ${logFile} || echo "No compression stats found"
|
|
echo -e "\n=== Duration ===\n"
|
|
grep "Duration:" ${logFile} || echo "No duration stats found"
|
|
grep "Throughput:" ${logFile} || echo "No throughput stats found"
|
|
echo -e "\n=== Repository ===\n"
|
|
${pkgs.borgbackup}/bin/borg info ${repoPath} --last 1 2>/dev/null || echo "Could not get repository info"
|
|
echo -e "\n=== Storage Space ===\n"
|
|
df -h ${repoPath} | grep -v "Filesystem" || echo "Could not get storage info"
|
|
} > ${logFile}.stats
|
|
STATS=$(cat ${logFile}.stats || echo "No stats available")
|
|
'';
|
|
|
|
# Unified backup service generator
|
|
mkBorgBackupService =
|
|
{
|
|
name,
|
|
title,
|
|
repo,
|
|
sourcePath,
|
|
keepDaily,
|
|
keepWeekly,
|
|
keepMonthly,
|
|
schedule,
|
|
}:
|
|
{
|
|
services."backup-${name}" = {
|
|
description = "Backup ${title} with Borg";
|
|
inherit (commonServiceConfig) path serviceConfig;
|
|
|
|
script = ''
|
|
${borgCommonSettings}
|
|
|
|
LOG_FILE="/tmp/borg-${name}-backup-$(date +%Y%m%d-%H%M%S).log"
|
|
${initRepo repo}
|
|
|
|
echo "Starting ${title} backup at $(date)" > $LOG_FILE
|
|
ARCHIVE_NAME="${name}-$(date +%Y-%m-%d_%H%M%S)"
|
|
START_TIME=$(date +%s)
|
|
|
|
${pkgs.borgbackup}/bin/borg create \
|
|
--stats \
|
|
--compression zstd,15 \
|
|
--exclude '*.tmp' \
|
|
--exclude '*/tmp/*' \
|
|
${repo}::$ARCHIVE_NAME \
|
|
${sourcePath} >> $LOG_FILE 2>&1
|
|
|
|
BACKUP_STATUS=$?
|
|
END_TIME=$(date +%s)
|
|
DURATION=$((END_TIME - START_TIME))
|
|
echo "Total time: $DURATION seconds ($(date -d@$DURATION -u +%H:%M:%S))" >> $LOG_FILE
|
|
|
|
${extractBorgStats "$LOG_FILE" "${repo}"}
|
|
|
|
echo -e "\nPruning old backups..." >> $LOG_FILE
|
|
${pkgs.borgbackup}/bin/borg prune \
|
|
--keep-daily ${toString keepDaily} \
|
|
--keep-weekly ${toString keepWeekly} \
|
|
--keep-monthly ${toString keepMonthly} \
|
|
${repo} >> $LOG_FILE 2>&1
|
|
|
|
PRUNE_STATUS=$?
|
|
|
|
echo -e "\nRemaining archives after pruning:" >> $LOG_FILE
|
|
${pkgs.borgbackup}/bin/borg list ${repo} >> $LOG_FILE 2>&1 || true
|
|
|
|
if [ $BACKUP_STATUS -eq 0 ] && [ $PRUNE_STATUS -eq 0 ]; then
|
|
${sendNotification "✅ ${title} Backup Complete" "${title} backup completed successfully on $(hostname) at $(date)\nDuration: $(date -d@$DURATION -u +%H:%M:%S)\n\n$STATS"}
|
|
else
|
|
${sendNotification "❌ ${title} Backup Failed" "${title} backup failed on $(hostname) at $(date)\n\nBackup Status: $BACKUP_STATUS\nPrune Status: $PRUNE_STATUS\n\nPartial Stats:\n$STATS\n\nSee $LOG_FILE for details"}
|
|
fi
|
|
|
|
rm -f $LOG_FILE.stats
|
|
exit $BACKUP_STATUS
|
|
'';
|
|
};
|
|
|
|
timers."backup-${name}" = {
|
|
description = "Timer for ${title} Backup";
|
|
wantedBy = [ "timers.target" ];
|
|
timerConfig = {
|
|
OnCalendar = schedule;
|
|
Persistent = true;
|
|
RandomizedDelaySec = "5min";
|
|
};
|
|
};
|
|
};
|
|
|
|
# Common service configuration
|
|
commonServiceConfig = {
|
|
path = commonBorgPath;
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
IOSchedulingClass = "idle";
|
|
CPUSchedulingPolicy = "idle";
|
|
Nice = 19;
|
|
};
|
|
};
|
|
|
|
in
|
|
{
|
|
environment.systemPackages = with pkgs; [
|
|
borgbackup
|
|
apprise
|
|
];
|
|
|
|
systemd = lib.mkMerge [
|
|
(mkBorgBackupService {
|
|
name = "docker-storage";
|
|
title = "Docker Storage";
|
|
repo = dockerStorageRepo;
|
|
sourcePath = "/mnt/drive1/DockerStorage";
|
|
keepDaily = 7;
|
|
keepWeekly = 4;
|
|
keepMonthly = 3;
|
|
schedule = "Mon *-*-* 04:00:00";
|
|
})
|
|
|
|
(mkBorgBackupService {
|
|
name = "forgejo";
|
|
title = "Forgejo";
|
|
repo = forgejoRepo;
|
|
sourcePath = "/pool/forgejo";
|
|
keepDaily = 14;
|
|
keepWeekly = 4;
|
|
keepMonthly = 3;
|
|
schedule = "*-*-1/2 04:00:00";
|
|
})
|
|
];
|
|
}
|