Implements a new module for backing up LNbits data using Borg. This module automates hourly backups, encrypts the data, and provides point-in-time recovery. It includes scripts for listing, restoring, and mounting backups. A comprehensive setup guide is provided in the documentation. The configuration allows specifying the Borg repository location, schedule, compression settings, retention policy, and SSH key for secure access.
177 lines
4.9 KiB
Nix
177 lines
4.9 KiB
Nix
{ config, lib, pkgs, ... }:
|
|
|
|
with lib;
|
|
|
|
let
|
|
cfg = config.services.lnbits-backup;
|
|
|
|
in {
|
|
options.services.lnbits-backup = {
|
|
enable = mkEnableOption "LNbits Borg Backup Service";
|
|
|
|
dataPath = mkOption {
|
|
type = types.path;
|
|
default = "/var/lib/lnbits/data";
|
|
description = "Path to LNbits data directory";
|
|
};
|
|
|
|
repository = mkOption {
|
|
type = types.str;
|
|
example = "ssh://borg@192.168.1.100//mnt/my_ssd/borg-backups/lnbits";
|
|
description = "Borg repository location (SSH URL or user@host:/path format)";
|
|
};
|
|
|
|
schedule = mkOption {
|
|
type = types.str;
|
|
default = "hourly";
|
|
description = "Backup schedule (hourly, daily, or systemd timer format)";
|
|
};
|
|
|
|
compression = mkOption {
|
|
type = types.str;
|
|
default = "auto,lz4";
|
|
description = "Compression: auto,lz4 (Pi), auto,zstd (RockPro64). 'auto' skips already-compressed files.";
|
|
};
|
|
|
|
passphraseFile = mkOption {
|
|
type = types.path;
|
|
default = "/root/secrets/borg-passphrase";
|
|
description = "Path to Borg passphrase file";
|
|
};
|
|
|
|
sshKeyFile = mkOption {
|
|
type = types.path;
|
|
default = "/root/.ssh/borg_backup_key";
|
|
description = "SSH private key for repository access";
|
|
};
|
|
|
|
retention = {
|
|
hourly = mkOption { type = types.int; default = 24; };
|
|
daily = mkOption { type = types.int; default = 7; };
|
|
weekly = mkOption { type = types.int; default = 4; };
|
|
monthly = mkOption { type = types.int; default = 6; };
|
|
};
|
|
|
|
alertEmail = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
description = "Email for alerts (optional)";
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
services.borgbackup.jobs.lnbits = {
|
|
paths = [
|
|
cfg.dataPath
|
|
"/tmp/lnbits-snapshot.sqlite3"
|
|
];
|
|
|
|
exclude = [ "*.log" "*.pyc" "__pycache__" "*.tmp" ];
|
|
repo = cfg.repository;
|
|
|
|
encryption = {
|
|
mode = "repokey-blake2";
|
|
passCommand = "cat ${cfg.passphraseFile}";
|
|
};
|
|
|
|
compression = cfg.compression;
|
|
startAt = cfg.schedule;
|
|
persistentTimer = true; # Run missed backups after reboots
|
|
|
|
prune.keep = {
|
|
inherit (cfg.retention) hourly daily weekly monthly;
|
|
};
|
|
|
|
preHook = ''
|
|
echo "=== LNbits Backup Starting: $(date) ==="
|
|
|
|
if [ -f "${cfg.dataPath}/database.sqlite3" ]; then
|
|
echo "Creating SQLite snapshot..."
|
|
${pkgs.sqlite}/bin/sqlite3 "${cfg.dataPath}/database.sqlite3" \
|
|
".backup '/tmp/lnbits-snapshot.sqlite3'"
|
|
|
|
SIZE=$(stat -c%s "/tmp/lnbits-snapshot.sqlite3")
|
|
echo "Snapshot created: $SIZE bytes"
|
|
|
|
if [ "$SIZE" -lt 1000 ]; then
|
|
echo "ERROR: Snapshot too small!"
|
|
rm -f /tmp/lnbits-snapshot.sqlite3
|
|
exit 1
|
|
fi
|
|
else
|
|
echo "ERROR: Database not found!"
|
|
exit 1
|
|
fi
|
|
'';
|
|
|
|
postHook = ''
|
|
rm -f /tmp/lnbits-snapshot.sqlite3
|
|
|
|
# Weekly integrity check (Sundays)
|
|
if [ $(date +%u) -eq 7 ]; then
|
|
echo "Running weekly integrity check..."
|
|
${pkgs.borgbackup}/bin/borg check --repository-only ${cfg.repository}
|
|
fi
|
|
|
|
echo "=== Backup Complete: $(date) ==="
|
|
'';
|
|
|
|
environment = {
|
|
BORG_RSH = "ssh -i ${cfg.sshKeyFile} -o StrictHostKeyChecking=accept-new";
|
|
};
|
|
};
|
|
|
|
# Install required packages and helper commands
|
|
environment.systemPackages = with pkgs; [
|
|
borgbackup
|
|
sqlite
|
|
|
|
(writeShellScriptBin "lnbits-borg-list" ''
|
|
export BORG_PASSPHRASE=$(cat ${cfg.passphraseFile})
|
|
export BORG_RSH="ssh -i ${cfg.sshKeyFile}"
|
|
${borgbackup}/bin/borg list ${cfg.repository} "$@"
|
|
'')
|
|
|
|
(writeShellScriptBin "lnbits-borg-info" ''
|
|
export BORG_PASSPHRASE=$(cat ${cfg.passphraseFile})
|
|
export BORG_RSH="ssh -i ${cfg.sshKeyFile}"
|
|
${borgbackup}/bin/borg info ${cfg.repository} "$@"
|
|
'')
|
|
|
|
(writeShellScriptBin "lnbits-borg-restore" ''
|
|
if [ $# -lt 1 ]; then
|
|
echo "Usage: lnbits-borg-restore <archive-name> [destination]"
|
|
echo ""
|
|
echo "Available archives:"
|
|
lnbits-borg-list
|
|
exit 1
|
|
fi
|
|
|
|
ARCHIVE="$1"
|
|
DEST="''${2:-/tmp/lnbits-restore}"
|
|
|
|
export BORG_PASSPHRASE=$(cat ${cfg.passphraseFile})
|
|
export BORG_RSH="ssh -i ${cfg.sshKeyFile}"
|
|
|
|
mkdir -p "$DEST"
|
|
cd "$DEST"
|
|
echo "Restoring to: $DEST"
|
|
${borgbackup}/bin/borg extract ${cfg.repository}::$ARCHIVE
|
|
echo "Done! Restored files in: $DEST"
|
|
'')
|
|
|
|
(writeShellScriptBin "lnbits-borg-mount" ''
|
|
MOUNT="''${1:-/mnt/borg-browse}"
|
|
|
|
export BORG_PASSPHRASE=$(cat ${cfg.passphraseFile})
|
|
export BORG_RSH="ssh -i ${cfg.sshKeyFile}"
|
|
|
|
mkdir -p "$MOUNT"
|
|
${borgbackup}/bin/borg mount ${cfg.repository} "$MOUNT"
|
|
echo "Mounted at: $MOUNT"
|
|
echo "Unmount: borg umount $MOUNT"
|
|
'')
|
|
];
|
|
};
|
|
}
|
|
|