97 lines
3 KiB
Nix
97 lines
3 KiB
Nix
{ config, pkgs, lib, ... }:
|
|
let
|
|
backupScript = pkgs.writeShellScript "wiki-backup" ''
|
|
set -euo pipefail
|
|
|
|
BACKUP_DIR="/var/backups/mysql"
|
|
TEXTFILE_DIR="/var/lib/prometheus-node-exporter/textfile"
|
|
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
|
|
|
# Cleanup old local backups (keep 7 days)
|
|
find "$BACKUP_DIR" -maxdepth 1 -type d -mtime +7 -exec rm -rf {} + 2>/dev/null || true
|
|
|
|
# Dump database with mydumper
|
|
echo "Starting database dump..."
|
|
${pkgs.mydumper}/bin/mydumper \
|
|
--database noisebridge_mediawiki \
|
|
--outputdir "$BACKUP_DIR/$TIMESTAMP" \
|
|
--threads 2 \
|
|
--compress \
|
|
--routines \
|
|
--triggers \
|
|
--events \
|
|
--logfile "$BACKUP_DIR/mydumper.log"
|
|
|
|
# Sync to Backblaze B2
|
|
echo "Syncing to Backblaze B2..."
|
|
export RCLONE_CONFIG_B2_TYPE=b2
|
|
export RCLONE_CONFIG_B2_ACCOUNT=$(cat ${config.age.secrets.b2-credentials.path} | ${pkgs.jq}/bin/jq -r .keyID)
|
|
export RCLONE_CONFIG_B2_KEY=$(cat ${config.age.secrets.b2-credentials.path} | ${pkgs.jq}/bin/jq -r .applicationKey)
|
|
|
|
SYNC_SUCCESS=0
|
|
${pkgs.rclone}/bin/rclone sync "$BACKUP_DIR" b2:noisebridge-wiki-backup/mysql/ \
|
|
--transfers 4 \
|
|
--checkers 8 \
|
|
--b2-hard-delete \
|
|
&& SYNC_SUCCESS=1
|
|
|
|
# Sync uploaded images to B2
|
|
${pkgs.rclone}/bin/rclone sync /var/lib/mediawiki/images/ b2:noisebridge-wiki-backup/images/ \
|
|
--transfers 4 \
|
|
--checkers 8 \
|
|
|| SYNC_SUCCESS=0
|
|
|
|
# Back up Tor hidden service keys (losing these = losing the .onion address)
|
|
${pkgs.rclone}/bin/rclone sync /var/lib/tor/onion/ b2:noisebridge-wiki-backup/tor-keys/ \
|
|
--transfers 1 \
|
|
|| true
|
|
|
|
# Write metrics for Prometheus textfile collector (no leading whitespace!)
|
|
cat > "$TEXTFILE_DIR/backup.prom" <<'METRICS'
|
|
# HELP backup_latest_timestamp_seconds Unix timestamp of latest backup
|
|
# TYPE backup_latest_timestamp_seconds gauge
|
|
METRICS
|
|
echo "backup_latest_timestamp_seconds $(date +%s)" >> "$TEXTFILE_DIR/backup.prom"
|
|
cat >> "$TEXTFILE_DIR/backup.prom" <<METRICS
|
|
# HELP backup_b2_sync_success Whether the last B2 sync succeeded (1=success, 0=failure)
|
|
# TYPE backup_b2_sync_success gauge
|
|
backup_b2_sync_success $SYNC_SUCCESS
|
|
METRICS
|
|
|
|
echo "Backup complete."
|
|
'';
|
|
in
|
|
{
|
|
systemd.services.wiki-backup = {
|
|
description = "Wiki database and image backup to Backblaze B2";
|
|
after = [ "mysql.service" ];
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
ExecStart = backupScript;
|
|
User = "root";
|
|
IOSchedulingClass = "idle";
|
|
CPUSchedulingPolicy = "idle";
|
|
};
|
|
};
|
|
|
|
systemd.timers.wiki-backup = {
|
|
description = "Daily wiki backup timer";
|
|
wantedBy = [ "timers.target" ];
|
|
timerConfig = {
|
|
OnCalendar = "*-*-* 04:00:00";
|
|
Persistent = true;
|
|
RandomizedDelaySec = "15m";
|
|
};
|
|
};
|
|
|
|
systemd.tmpfiles.rules = [
|
|
"d /var/backups/mysql 0750 root root -"
|
|
];
|
|
|
|
age.secrets.b2-credentials = {
|
|
file = ../../secrets/b2-credentials.age;
|
|
owner = "root";
|
|
group = "root";
|
|
mode = "0400";
|
|
};
|
|
}
|