add backup thing

This commit is contained in:
Osman Faruk Bayram 2025-12-21 19:30:01 +03:00
parent f7414548ec
commit 07c7e98316
10 changed files with 302 additions and 0 deletions

View file

@ -15,6 +15,20 @@
immich.enable = true;
actual.enable = true;
# seafile.enable = true;
# Backup server - exposes data for pull-based backups
backup-server = {
enable = true;
zfsSnapshots = {
enable = true;
# Keep snapshots for point-in-time recovery
frequent = 4; # 4 x 15min = 1 hour of frequent snapshots
hourly = 24; # 24 hours
daily = 7; # 1 week
weekly = 4; # 1 month
monthly = 12; # 1 year
};
};
};
hardware = {

View file

@ -14,6 +14,20 @@
desktopEnvironment.gnome.enable = true;
hardware.systemd-boot.enable = false; # Mobile devices use different bootloader
programs.graphical.enable = false;
services = {
# Backup client - pulls vaultwarden backup from apollo
backup-client = {
enable = true;
backups = {
apollo-vaultwarden = {
remoteHost = "apollo";
localPath = "/var/backups/apollo-vaultwarden";
services = [ "vaultwarden" ];
};
};
};
};
};
# mobile-nixos needs aliases (uses nettools instead of net-tools)

View file

@ -18,6 +18,18 @@
wanikani-bypass-lessons.enable = true;
wanikani-fetch-data.enable = true;
wanikani-stats.enable = true;
# Backup client - pulls full backup from apollo
backup-client = {
enable = true;
backups = {
apollo-full = {
remoteHost = "apollo"; # Tailscale hostname
localPath = "/var/backups/apollo";
fullBackup = true;
};
};
};
};
desktopEnvironment.plasma.enable = true;
programs.graphical.enable = false;

View file

@ -10,6 +10,20 @@
emulation.aarch64.enable = true;
hardware.sound.enable = true;
programs.steam.enable = true;
services = {
# Backup client - pulls vaultwarden backup from apollo
backup-client = {
enable = true;
backups = {
apollo-vaultwarden = {
remoteHost = "apollo";
localPath = "/var/backups/apollo-vaultwarden";
services = [ "vaultwarden" ];
};
};
};
};
};
networking.hostName = "tartarus";

View file

@ -8,6 +8,18 @@
hydra.enable = true;
atticd.enable = true;
cloudflared.enable = true;
# Backup client - pulls full backup from apollo
backup-client = {
enable = true;
backups = {
apollo-full = {
remoteHost = "apollo"; # Tailscale hostname
localPath = "/var/backups/apollo";
fullBackup = true;
};
};
};
};
};

View file

@ -25,6 +25,18 @@
};
services = {
ollama.enable = true;
# Backup client - pulls vaultwarden backup from apollo
backup-client = {
enable = true;
backups = {
apollo-vaultwarden = {
remoteHost = "apollo";
localPath = "/var/backups/apollo-vaultwarden";
services = [ "vaultwarden" ];
};
};
};
};
i18n.enable = true;
};

View file

@ -147,6 +147,100 @@
wanikani-bypass-lessons.enable = lib.mkEnableOption "wanikani-bypass-lessons";
wanikani-fetch-data.enable = lib.mkEnableOption "wanikani-fetch-data";
wanikani-stats.enable = lib.mkEnableOption "wanikani-stats";
# Backup server - exposes data for pull-based backups
backup-server = {
enable = lib.mkEnableOption "backup server (exposes data for pull-based backups)";
zfsSnapshots = {
enable = lib.mkEnableOption "automatic ZFS snapshots of /persist";
frequent = lib.mkOption {
type = lib.types.int;
default = 4;
description = "Number of frequent (15-min) snapshots to keep";
};
hourly = lib.mkOption {
type = lib.types.int;
default = 24;
description = "Number of hourly snapshots to keep";
};
daily = lib.mkOption {
type = lib.types.int;
default = 7;
description = "Number of daily snapshots to keep";
};
weekly = lib.mkOption {
type = lib.types.int;
default = 4;
description = "Number of weekly snapshots to keep";
};
monthly = lib.mkOption {
type = lib.types.int;
default = 12;
description = "Number of monthly snapshots to keep";
};
};
};
# Backup client - pulls backups from remote servers
backup-client = {
enable = lib.mkEnableOption "backup client (pulls data from remote servers)";
backups = lib.mkOption {
type = lib.types.attrsOf (
lib.types.submodule {
options = {
remoteHost = lib.mkOption {
type = lib.types.str;
description = "Remote host to backup from (e.g., apollo.tail-scale.ts.net)";
};
remoteUser = lib.mkOption {
type = lib.types.str;
default = "root";
description = "Remote user for SSH connection";
};
localPath = lib.mkOption {
type = lib.types.str;
description = "Local path where backups will be stored";
};
fullBackup = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to backup the entire /persist directory";
};
services = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "List of services to backup (e.g., ['vaultwarden', 'immich'])";
example = [
"vaultwarden"
"immich"
"forgejo"
];
};
schedule = lib.mkOption {
type = lib.types.str;
default = "daily";
description = "Backup schedule (systemd timer format)";
example = "daily";
};
};
}
);
default = { };
description = "Backup configurations";
};
};
};
# Hardware

View file

@ -0,0 +1,95 @@
# 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";
}
];
};
}

View file

@ -0,0 +1,33 @@
# Backup server - exposes data for backup clients to pull
# Enables ZFS snapshots for local point-in-time recovery
{
config,
lib,
...
}:
let
cfg = config.osbmModules.services.backup-server;
in
{
config = lib.mkIf cfg.enable {
# Enable ZFS auto-snapshots if requested
services.zfs.autoSnapshot = lib.mkIf cfg.zfsSnapshots.enable {
enable = true;
inherit (cfg.zfsSnapshots)
frequent
hourly
daily
weekly
monthly
;
};
# Ensure SSH is enabled for backup access
assertions = [
{
assertion = config.services.openssh.enable;
message = "backup-server requires openssh to be enabled";
}
];
};
}

View file

@ -3,6 +3,8 @@
./actual.nix
./anubis.nix
./atticd.nix
./backup-client.nix
./backup-server.nix
./cloudflared.nix
./ollama.nix
./openssh.nix