diff --git a/modules/homelab/arr/bazarr/default.nix b/modules/homelab/arr/bazarr/default.nix index b134693..3d9b4c0 100644 --- a/modules/homelab/arr/bazarr/default.nix +++ b/modules/homelab/arr/bazarr/default.nix @@ -92,7 +92,7 @@ in icon = "di:${service}"; }]; # add to backups - system.backups.baks = { + homelab.baks = { ${service} = { paths = [ cfg.data_dir ]; }; }; }; diff --git a/modules/homelab/arr/prowlarr/default.nix b/modules/homelab/arr/prowlarr/default.nix index b587432..bd3b154 100644 --- a/modules/homelab/arr/prowlarr/default.nix +++ b/modules/homelab/arr/prowlarr/default.nix @@ -94,7 +94,7 @@ in icon = "di:${service}"; }]; # add to backups - system.backups.baks = { + homelab.baks = { ${service} = { paths = [ cfg.data_dir ]; }; }; }; diff --git a/modules/homelab/arr/radarr/default.nix b/modules/homelab/arr/radarr/default.nix index 5138fc2..23eadf1 100644 --- a/modules/homelab/arr/radarr/default.nix +++ b/modules/homelab/arr/radarr/default.nix @@ -97,7 +97,7 @@ in icon = "di:${service}"; }]; # add to backups - system.backups.baks = { + homelab.baks = { ${service} = { paths = [ cfg.data_dir ]; }; }; }; diff --git a/modules/homelab/arr/sonarr/default.nix b/modules/homelab/arr/sonarr/default.nix index 66909ef..266446e 100644 --- a/modules/homelab/arr/sonarr/default.nix +++ b/modules/homelab/arr/sonarr/default.nix @@ -95,7 +95,7 @@ in icon = "di:${service}"; }]; # add to backups - system.backups.baks = { + homelab.baks = { ${service} = { paths = [ cfg.data_dir ]; }; }; }; diff --git a/modules/homelab/audiobookshelf/default.nix b/modules/homelab/audiobookshelf/default.nix index 2c934e6..8547a31 100644 --- a/modules/homelab/audiobookshelf/default.nix +++ b/modules/homelab/audiobookshelf/default.nix @@ -98,7 +98,7 @@ in icon = "di:${service}"; }]; # add to backups - system.backups.baks = { + homelab.baks = { ${service} = { paths = [ cfg.data_dir ]; }; }; }; diff --git a/modules/homelab/caddy/default.nix b/modules/homelab/caddy/default.nix index 03305c9..fb883a1 100644 --- a/modules/homelab/caddy/default.nix +++ b/modules/homelab/caddy/default.nix @@ -95,7 +95,7 @@ in # add to backups - system.backups.baks = { + homelab.baks = { ${service} = { paths = [ cfg.data_dir ]; }; }; }; diff --git a/modules/homelab/default.nix b/modules/homelab/default.nix index 0903276..122591c 100644 --- a/modules/homelab/default.nix +++ b/modules/homelab/default.nix @@ -37,9 +37,15 @@ in type = lib.types.str; description = "base domain used for reverse proxy"; }; + baks = lib.mkOption { + type = lib.types.attrsOf (lib.types.attrsOf (lib.types.listOf lib.types.path)); + default = {}; + description = "backup jobs, nested attribute sets should be = paths []"; + }; backup_repo = lib.mkOption { type = lib.types.path; default = "/holocron/archives/homelab"; + description = "path to take daily backups to with borg!"; }; }; @@ -78,7 +84,7 @@ in }; }; - # backups with borg + # backups homelab with borg services.borgbackup.jobs.homelab = { archiveBaseName = "homelab"; repo = cfg.backup_repo; @@ -86,9 +92,10 @@ in compression = "auto,zstd"; startAt = "daily"; group = "archives"; - encryption.mode = "repokey"; + encryption.mode = "repokey-blake2"; encryption.passCommand = "cat ${config.sops.secrets."borg_passwd".path}"; - extraArgs = ["--verbose" "--list" "--filter AME" "--stats" "--show-rc" "--umask 0007"]; + extraArgs = ["--verbose" "--show-rc" "--umask" "0007"]; + extraCreateArgs = ["--list" "--stats" "--filter" "AME"]; prune.keep = { within = "1d"; # Keep all archives from the last day daily = 7; diff --git a/modules/homelab/gitea/default.nix b/modules/homelab/gitea/default.nix index 2876937..f1836d0 100644 --- a/modules/homelab/gitea/default.nix +++ b/modules/homelab/gitea/default.nix @@ -125,7 +125,7 @@ in }; # add to backups - system.backups.baks = { + homelab.baks = { ${service} = { paths = [ cfg.data_dir ]; }; }; }; diff --git a/modules/homelab/glance/default.nix b/modules/homelab/glance/default.nix index 2b5e6e1..1d33159 100644 --- a/modules/homelab/glance/default.nix +++ b/modules/homelab/glance/default.nix @@ -313,7 +313,7 @@ in }; # add to backups - system.backups.baks = { + homelab.baks = { ${service} = { paths = [ cfg.data_dir ]; }; diff --git a/modules/homelab/home/homeassistant/default.nix b/modules/homelab/home/homeassistant/default.nix index f1bfe9b..b83e8d9 100644 --- a/modules/homelab/home/homeassistant/default.nix +++ b/modules/homelab/home/homeassistant/default.nix @@ -109,7 +109,7 @@ in icon = "di:${nixservice}"; }]; # add to backups - system.backups.baks = { + homelab.baks = { ${service} = { paths = [ cfg.data_dir ]; }; }; }; diff --git a/modules/homelab/home/mosquitto/default.nix b/modules/homelab/home/mosquitto/default.nix index 9487e90..0ef5096 100644 --- a/modules/homelab/home/mosquitto/default.nix +++ b/modules/homelab/home/mosquitto/default.nix @@ -92,7 +92,7 @@ in }; # add to backups - system.backups.baks = { + homelab.baks = { ${service} = { paths = [ cfg.data_dir ]; }; }; }; diff --git a/modules/homelab/home/zigbee2mqtt/default.nix b/modules/homelab/home/zigbee2mqtt/default.nix index 6170c9b..a20c2fc 100644 --- a/modules/homelab/home/zigbee2mqtt/default.nix +++ b/modules/homelab/home/zigbee2mqtt/default.nix @@ -123,7 +123,7 @@ in }; # add to backups - system.backups.baks = { + homelab.baks = { ${service} = { paths = [ cfg.data_dir ]; }; }; }; diff --git a/modules/homelab/immich/default.nix b/modules/homelab/immich/default.nix index ec7b48b..b48c3b7 100644 --- a/modules/homelab/immich/default.nix +++ b/modules/homelab/immich/default.nix @@ -108,7 +108,7 @@ in { ]; # add to backups - system.backups.baks = { + homelab.baks = { #${service} = {paths = [cfg.data_dir "/var/lib/redis-immich" "/var/backup/postgresql/immich.sql.zstd"];}; ${service} = {paths = [cfg.data_dir "/var/backup/postgresql/immich.sql.zstd"];}; }; diff --git a/modules/homelab/jellyfin/default.nix b/modules/homelab/jellyfin/default.nix index 844afa1..0279c0a 100644 --- a/modules/homelab/jellyfin/default.nix +++ b/modules/homelab/jellyfin/default.nix @@ -93,7 +93,7 @@ in icon = "di:${service}"; }]; # add to backups - system.backups.baks = { + homelab.baks = { ${service} = { paths = [ cfg.data_dir ]; }; }; }; diff --git a/modules/homelab/minecraft_recpro/default.nix b/modules/homelab/minecraft_recpro/default.nix index 58afeaf..5e350fa 100644 --- a/modules/homelab/minecraft_recpro/default.nix +++ b/modules/homelab/minecraft_recpro/default.nix @@ -1,17 +1,32 @@ -{ pkgs, config, lib, ... }: - -let +{ + pkgs, + config, + lib, + ... +}: let service = "minecraft_recpro"; cfg = config.gameservers.${service}; sec = config.sops.secrets; servers = { - velocity = { data_dir = "/var/lib/gameservers/minecraft_recpro/velocity"; ram = "2G"; }; - smp = { data_dir = "/var/lib/gameservers/minecraft_recpro/smp"; ram = "12G"; }; - superflat = { data_dir = "/var/lib/gameservers/minecraft_recpro/superflat"; ram = "4G"; }; - bento = { data_dir = "/var/lib/gameservers/minecraft_recpro/bento"; ram = "2G"; }; + velocity = { + data_dir = "/var/lib/gameservers/minecraft_recpro/velocity"; + db_dumb_dir = "/var/backup/mysql/${service}_db.zst"; + ram = "2G"; + }; + smp = { + data_dir = "/var/lib/gameservers/minecraft_recpro/smp"; + ram = "12G"; + }; + superflat = { + data_dir = "/var/lib/gameservers/minecraft_recpro/superflat"; + ram = "4G"; + }; + bento = { + data_dir = "/var/lib/gameservers/minecraft_recpro/bento"; + ram = "2G"; + }; }; -in -{ +in { options.gameservers.${service} = { enable = lib.mkEnableOption "enables ${service}"; url = lib.mkOption { @@ -38,12 +53,16 @@ in type = lib.types.nullOr lib.types.str; default = "velocity"; }; + backup_repo = lib.mkOption { + type = lib.types.path; + default = "/holocron/archives/gameservers/minecraft/recpro_stack"; + description = "path to take hourly backups to with borg!"; + }; }; config = lib.mkIf cfg.enable { - # declare ${service} group - users.groups.minecraft = { gid = lib.mkForce cfg.ids; }; + users.groups.minecraft = {gid = lib.mkForce cfg.ids;}; # declare ${service} user users.users.minecraft = { @@ -55,37 +74,41 @@ in extraGroups = []; }; - systemd.tmpfiles.rules = lib.attrsets.mapAttrsToList (name: cfg: - "d ${cfg.data_dir} 0770 minecraft minecraft -" - ) servers; + systemd.tmpfiles.rules = + lib.attrsets.mapAttrsToList ( + name: cfg: "d ${cfg.data_dir} 0770 minecraft minecraft -" + ) + servers; # Create a systemd service per server running in tmux - systemd.services = lib.attrsets.mapAttrs (name: srv: { - description = "minecraft_recpro: ${name}"; - after = [ "network.target" ]; - wants = [ "network.target" ]; - serviceConfig = { - User = "minecraft"; - Group = "minecraft"; - WorkingDirectory = srv.data_dir; - UMask = "0007"; - ExecStart = "${pkgs.openjdk21}/bin/java -Xmx${srv.ram} -jar server.jar nogui"; - Restart = "on-failure"; - KillMode = "process"; - }; - wantedBy = [ "multi-user.target" ]; - }) servers; + systemd.services = + lib.attrsets.mapAttrs (name: srv: { + description = "minecraft_recpro: ${name}"; + after = ["network.target"]; + wants = ["network.target"]; + serviceConfig = { + User = "minecraft"; + Group = "minecraft"; + WorkingDirectory = srv.data_dir; + UMask = "0007"; + ExecStart = "${pkgs.openjdk21}/bin/java -Xmx${srv.ram} -jar server.jar nogui"; + Restart = "on-failure"; + KillMode = "process"; + }; + wantedBy = ["multi-user.target"]; + }) + servers; - environment.systemPackages = with pkgs; [ openjdk21 mcrcon ]; + environment.systemPackages = with pkgs; [openjdk21 mcrcon]; services.mysql = { enable = true; package = pkgs.mariadb; - ensureDatabases = [ "minecraft_recpro_db" ]; + ensureDatabases = ["minecraft_recpro_db"]; ensureUsers = [ { name = "minecraft"; - ensurePermissions = { "minecraft_recpro_db.*" = "ALL PRIVILEGES"; }; + ensurePermissions = {"minecraft_recpro_db.*" = "ALL PRIVILEGES";}; } ]; initialScript = pkgs.writeText "minecraft_recpro-init.sql" '' @@ -94,9 +117,9 @@ in FLUSH PRIVILEGES; ''; }; - + # open firewall - networking.firewall.allowedTCPPorts = [ 25777 25565 25566 25567 ]; + networking.firewall.allowedTCPPorts = [25777 25565 25566 25567]; sops.secrets = { "velocity_forwarding" = { @@ -109,17 +132,29 @@ in owner = "mysql"; group = "mysql"; }; - }; - # add to backups - system.backups.gameserver_baks = lib.listToAttrs ( - lib.mapAttrsToList (srv_name: cfg: - { - name = srv_name; # attribute key - value = { paths = [ cfg.data_dir "/var/backup/mysql/${service}_db.zst" ]; }; # attribute value - } - ) servers - ); + # backups minecraft_recpro with borg! + services.borgbackup.jobs.${service} = + lib.mapAttrs (name: srv: { + archiveBaseName = "${name}"; + repo = cfg.backup_repo; + paths = [srv.data_dir] ++ lib.optionals (srv ? db_dump_dir) [srv.db_path]; + compression = "auto,zstd"; + startAt = "*-*-* *:00:00"; + group = "archives"; + encryption.mode = "repokey-blake2"; + encryption.passCommand = "cat ${config.sops.secrets."borg_passwd".path}"; + extraArgs = ["--verbose" "--show-rc" "--umask" "0007"]; + extraCreateArgs = ["--list" "--stats" "--filter" "AME"]; + prune.keep = { + within = "1d"; # Keep all archives from the last day + hourly = 24; + daily = 7; + weekly = 12; + monthly = -1; # Keep at least one archive for each month + }; + }) + servers; }; } diff --git a/modules/homelab/motd/default.nix b/modules/homelab/motd/default.nix index 84e4211..36af4ee 100644 --- a/modules/homelab/motd/default.nix +++ b/modules/homelab/motd/default.nix @@ -94,13 +94,5 @@ in { programs.zsh.interactiveShellInit = '' /etc/motd ''; - - #environment.loginShellInit = '' - # if [ -x /etc/motd ]; then - # /etc/motd - # else - # cat /etc/motd - # fi - #''; }; } diff --git a/modules/homelab/qbittorrent/default.nix b/modules/homelab/qbittorrent/default.nix index e73b403..e2bb94f 100644 --- a/modules/homelab/qbittorrent/default.nix +++ b/modules/homelab/qbittorrent/default.nix @@ -131,7 +131,7 @@ in icon = "di:${service}"; }]; # add to backups - system.backups.baks = { + homelab.baks = { ${service} = { paths = [ cfg.data_dir ]; }; }; }; diff --git a/modules/homelab/uptime-kuma/default.nix b/modules/homelab/uptime-kuma/default.nix index 4f5a6d6..de03ca4 100644 --- a/modules/homelab/uptime-kuma/default.nix +++ b/modules/homelab/uptime-kuma/default.nix @@ -93,7 +93,7 @@ in icon = "di:${service}"; }]; # add to backups - system.backups.baks = { + homelab.baks = { ${service} = { paths = [ cfg.data_dir ]; }; }; }; diff --git a/modules/homelab/vaultwarden/default.nix b/modules/homelab/vaultwarden/default.nix index d3ba6db..e9c4079 100644 --- a/modules/homelab/vaultwarden/default.nix +++ b/modules/homelab/vaultwarden/default.nix @@ -113,7 +113,7 @@ in }; # add to backups - system.backups.baks = { + homelab.baks = { ${service} = { paths = [ cfg.data_dir ]; }; }; }; diff --git a/modules/homelab/yacreader/default.nix b/modules/homelab/yacreader/default.nix index 4e8982d..f403240 100644 --- a/modules/homelab/yacreader/default.nix +++ b/modules/homelab/yacreader/default.nix @@ -101,7 +101,7 @@ in icon = "di:yac-reader"; }]; # add to backups - system.backups.baks = { + homelab.baks = { ${service} = { paths = [ cfg.data_dir ]; }; }; }; diff --git a/modules/system/backups/default.nix b/modules/system/backups/default.nix index 79ff0f2..5db07d0 100644 --- a/modules/system/backups/default.nix +++ b/modules/system/backups/default.nix @@ -58,193 +58,6 @@ in { config = lib.mkIf (cfg.enable && cfg.baks != {}) { - # create and or set perms for repo dirs - systemd.tmpfiles.rules = [ - "d ${cfg.repo} 2770 root archives - -" - "d ${cfg.gameserver_repo} 2770 root archives - -" - ]; - - # create servie to backup services - systemd.services.backups = { - description = "backup services with borg!"; - path = [pkgs.borgbackup]; - serviceConfig = { - Type = "oneshot"; - User = "root"; - Group = "archives"; # make perms shake out - UMask = "0007"; # make perms shake out - # the actual script borg is using - ExecStart = pkgs.writeShellScript "borg-backup" '' - backup() { - set -euo pipefail - export BORG_PASSPHRASE="$(cat ${cfg.passwd_file})" - export BORG_REPO="${cfg.repo}" - timestamp="$(date +'%Y-%m-%d_%H:%M:%S')" - mode=split - - # init repo in needed - if ! borg info "$BORG_REPO" >/dev/null 2>&1; then - echo "Initializing Borg repo at $BORG_REPO" - borg init --encryption=repokey "$BORG_REPO" - fi - - borg break-lock "$BORG_REPO" || true - - echo "starting backup at $timestamp" - - if [ "$mode" = "split" ]; then - # loop for each backup - ${lib.concatStringsSep "\n\n" (lib.mapAttrsToList ( - bak_name: bak_paths: '' - echo "------------ Backing up ${bak_name} ------------" - archive="$timestamp-${bak_name}" - echo "backing up: ${lib.concatStringsSep " " bak_paths.paths} → $archive" - borg create \ - --verbose \ - --filter AME \ - --list \ - --stats \ - --show-rc \ - --compression lz4 \ - "$BORG_REPO::$archive" \ - ${lib.concatStringsSep " " bak_paths.paths} - echo "pruning old backups for ${bak_name}..." - borg prune -v --list "$BORG_REPO" \ - --glob-archives "*-${bak_name}" \ - --keep-daily=7 \ - --keep-weekly=52 \ - --keep-monthly=-1 - echo "backup run complete at \"$BORG_REPO::$archive\"" - '' - ) - cfg.baks)} - exit 0 - else - # flatten all paths from cfg.baks into one big list - all_paths="${ - lib.concatStringsSep " " - (lib.flatten - (lib.mapAttrsToList (_: bak: bak.paths) cfg.baks)) - }" - borg create \ - --verbose \ - --filter AME \ - --list \ - --stats \ - --show-rc \ - --compression lzma,9 \ - "$BORG_REPO::$timestamp-${toString config.networking.hostName}" \ - $all_paths - - echo "pruning old backups for ${toString config.networking.hostName}..." - borg prune -v --list "$BORG_REPO" \ - --glob-archives "*-${toString config.networking.hostName}" \ - --keep-daily=7 \ - --keep-weekly=52 \ - --keep-monthly=-1 - echo "backup run complete at \"$BORG_REPO::${toString config.networking.hostName}\"" - exit 0 - fi - } - start_time=$(date +%s) - backup - end_time=$(date +%s) - exec_time=$((end_time - start_time)) - cpu_usage=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1"%"}') - echo "" - echo "backup stats:" - echo "exec time: $exec_time" - echo "cpu usage: $cpu_usage" - ''; - }; - }; - # create timer to run backups daily - systemd.timers.backups = { - description = "daily borg backup timer"; - wantedBy = ["timers.target"]; - timerConfig = { - OnCalendar = "04:00"; - Persistent = true; - }; - }; - - # create servie to backup gameservers (back these up hourly) - systemd.services.gameserver_backups = { - description = "backup services with borg!"; - path = [pkgs.borgbackup]; - serviceConfig = { - Type = "oneshot"; - User = "root"; - Group = "archives"; # make perms shake out - UMask = "0007"; # make perms shake out - # the actual script borg is using - ExecStart = pkgs.writeShellScript "borg-gameserver_backup" '' - backup() { - set -euo pipefail - export BORG_PASSPHRASE="$(cat ${cfg.passwd_file})" - export BORG_REPO="${cfg.gameserver_repo}" - timestamp="$(date +'%Y-%m-%d_%H:%M:%S')" - - # init repo in needed - if ! borg info "$BORG_REPO" >/dev/null 2>&1; then - echo "Initializing Borg repo at $BORG_REPO" - borg init --encryption=repokey "$BORG_REPO" - fi - - borg break-lock "$BORG_REPO" || true - - echo "starting backup at $timestamp" - - # loop for each backup - ${lib.concatStringsSep "\n\n" (lib.mapAttrsToList ( - bak_name: bak_paths: '' - echo "------------ Backing up ${bak_name} ------------" - archive="$timestamp-${bak_name}" - echo "backing up: ${lib.concatStringsSep " " bak_paths.paths} → $archive" - borg create \ - --verbose \ - --filter AME \ - --list \ - --stats \ - --show-rc \ - --compression lz4 \ - "$BORG_REPO::$archive" \ - ${lib.concatStringsSep " " bak_paths.paths} - echo "pruning old backups for ${bak_name}..." - borg prune -v --list "$BORG_REPO" \ - --glob-archives "*-${bak_name}" \ - --keep-hourly=24 \ - --keep-daily=7 \ - --keep-weekly=12 \ - --keep-monthly=12 - echo "backup run complete at \"$BORG_REPO::$archive\"" - '' - ) - cfg.gameserver_baks)} - exit 0 - } - start_time=$(date +%s) - backup - end_time=$(date +%s) - exec_time=$((end_time - start_time)) - cpu_usage=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1"%"}') - echo "" - echo "backup stats:" - echo "exec time: $exec_time" - echo "cpu usage: $cpu_usage" - ''; - }; - }; - # create timer to run backups daily - systemd.timers.gameserver_backups = { - description = "daily borg backup timer"; - wantedBy = ["timers.target"]; - timerConfig = { - OnCalendar = "*-*-* *:00:00"; # every hour, at :01 (one min after db dump) - Persistent = true; - }; - }; - # db backups services.mysqlBackup = lib.mkIf config.services.mysql.enable { # mc servers use this @@ -265,9 +78,6 @@ in { #databases = config.services.postgresql.ensureDatabases; # set to all databases defined in esure databases }; - # install borg binary - environment.systemPackages = with pkgs; [borgbackup tree]; - # declare secret for repo password sops.secrets = { "borg_passwd" = { diff --git a/modules/system/backups/default.nix.old b/modules/system/backups/default.nix.old new file mode 100644 index 0000000..79ff0f2 --- /dev/null +++ b/modules/system/backups/default.nix.old @@ -0,0 +1,279 @@ +{ + config, + lib, + pkgs, + ... +}: +/* +this module enables a backup script made with borg! +to use import & set the options below +to declare a backup add the following code +to a module and it will backup all listed paths +in a borg archive to the specified repo + + | <3yy> | + V V + system.backups.baks = { + ${service} = { paths = [ cfg.data_dir ]; }; + }; +*/ +let + cfg = config.system.backups; + sec = config.sops.secrets; + borg = "${pkgs.borgbackup}/bin/borg"; +in { + options.system.backups = { + enable = lib.mkEnableOption "enables backups with borg"; + baks = lib.mkOption { + type = lib.types.attrsOf (lib.types.attrsOf (lib.types.listOf lib.types.path)); + default = {}; + description = "backup jobs, nested attribute sets should be = paths []"; + }; + gameserver_baks = lib.mkOption { + type = lib.types.attrsOf (lib.types.attrsOf (lib.types.listOf lib.types.path)); + default = {}; + description = "backup jobs for game servers, nested attribute sets should be = paths []"; + }; + repo = lib.mkOption { + type = lib.types.path; + default = "/holocron/archives/devices/snowbelle"; + description = "borg repository path"; + }; + gameserver_repo = lib.mkOption { + type = lib.types.path; + default = "/holocron/archives/gameservers/borg"; + description = "borg repository path"; + }; + passwd_file = lib.mkOption { + type = lib.types.path; + default = sec."borg_passwd".path; + description = "borg repository passphrase file"; + }; + mode = lib.mkOption { + type = lib.types.str; + default = "split"; # "all" + description = "choice between creating one archive of all paths or one archive per service"; + }; + }; + + config = lib.mkIf (cfg.enable && cfg.baks != {}) { + + # create and or set perms for repo dirs + systemd.tmpfiles.rules = [ + "d ${cfg.repo} 2770 root archives - -" + "d ${cfg.gameserver_repo} 2770 root archives - -" + ]; + + # create servie to backup services + systemd.services.backups = { + description = "backup services with borg!"; + path = [pkgs.borgbackup]; + serviceConfig = { + Type = "oneshot"; + User = "root"; + Group = "archives"; # make perms shake out + UMask = "0007"; # make perms shake out + # the actual script borg is using + ExecStart = pkgs.writeShellScript "borg-backup" '' + backup() { + set -euo pipefail + export BORG_PASSPHRASE="$(cat ${cfg.passwd_file})" + export BORG_REPO="${cfg.repo}" + timestamp="$(date +'%Y-%m-%d_%H:%M:%S')" + mode=split + + # init repo in needed + if ! borg info "$BORG_REPO" >/dev/null 2>&1; then + echo "Initializing Borg repo at $BORG_REPO" + borg init --encryption=repokey "$BORG_REPO" + fi + + borg break-lock "$BORG_REPO" || true + + echo "starting backup at $timestamp" + + if [ "$mode" = "split" ]; then + # loop for each backup + ${lib.concatStringsSep "\n\n" (lib.mapAttrsToList ( + bak_name: bak_paths: '' + echo "------------ Backing up ${bak_name} ------------" + archive="$timestamp-${bak_name}" + echo "backing up: ${lib.concatStringsSep " " bak_paths.paths} → $archive" + borg create \ + --verbose \ + --filter AME \ + --list \ + --stats \ + --show-rc \ + --compression lz4 \ + "$BORG_REPO::$archive" \ + ${lib.concatStringsSep " " bak_paths.paths} + echo "pruning old backups for ${bak_name}..." + borg prune -v --list "$BORG_REPO" \ + --glob-archives "*-${bak_name}" \ + --keep-daily=7 \ + --keep-weekly=52 \ + --keep-monthly=-1 + echo "backup run complete at \"$BORG_REPO::$archive\"" + '' + ) + cfg.baks)} + exit 0 + else + # flatten all paths from cfg.baks into one big list + all_paths="${ + lib.concatStringsSep " " + (lib.flatten + (lib.mapAttrsToList (_: bak: bak.paths) cfg.baks)) + }" + borg create \ + --verbose \ + --filter AME \ + --list \ + --stats \ + --show-rc \ + --compression lzma,9 \ + "$BORG_REPO::$timestamp-${toString config.networking.hostName}" \ + $all_paths + + echo "pruning old backups for ${toString config.networking.hostName}..." + borg prune -v --list "$BORG_REPO" \ + --glob-archives "*-${toString config.networking.hostName}" \ + --keep-daily=7 \ + --keep-weekly=52 \ + --keep-monthly=-1 + echo "backup run complete at \"$BORG_REPO::${toString config.networking.hostName}\"" + exit 0 + fi + } + start_time=$(date +%s) + backup + end_time=$(date +%s) + exec_time=$((end_time - start_time)) + cpu_usage=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1"%"}') + echo "" + echo "backup stats:" + echo "exec time: $exec_time" + echo "cpu usage: $cpu_usage" + ''; + }; + }; + # create timer to run backups daily + systemd.timers.backups = { + description = "daily borg backup timer"; + wantedBy = ["timers.target"]; + timerConfig = { + OnCalendar = "04:00"; + Persistent = true; + }; + }; + + # create servie to backup gameservers (back these up hourly) + systemd.services.gameserver_backups = { + description = "backup services with borg!"; + path = [pkgs.borgbackup]; + serviceConfig = { + Type = "oneshot"; + User = "root"; + Group = "archives"; # make perms shake out + UMask = "0007"; # make perms shake out + # the actual script borg is using + ExecStart = pkgs.writeShellScript "borg-gameserver_backup" '' + backup() { + set -euo pipefail + export BORG_PASSPHRASE="$(cat ${cfg.passwd_file})" + export BORG_REPO="${cfg.gameserver_repo}" + timestamp="$(date +'%Y-%m-%d_%H:%M:%S')" + + # init repo in needed + if ! borg info "$BORG_REPO" >/dev/null 2>&1; then + echo "Initializing Borg repo at $BORG_REPO" + borg init --encryption=repokey "$BORG_REPO" + fi + + borg break-lock "$BORG_REPO" || true + + echo "starting backup at $timestamp" + + # loop for each backup + ${lib.concatStringsSep "\n\n" (lib.mapAttrsToList ( + bak_name: bak_paths: '' + echo "------------ Backing up ${bak_name} ------------" + archive="$timestamp-${bak_name}" + echo "backing up: ${lib.concatStringsSep " " bak_paths.paths} → $archive" + borg create \ + --verbose \ + --filter AME \ + --list \ + --stats \ + --show-rc \ + --compression lz4 \ + "$BORG_REPO::$archive" \ + ${lib.concatStringsSep " " bak_paths.paths} + echo "pruning old backups for ${bak_name}..." + borg prune -v --list "$BORG_REPO" \ + --glob-archives "*-${bak_name}" \ + --keep-hourly=24 \ + --keep-daily=7 \ + --keep-weekly=12 \ + --keep-monthly=12 + echo "backup run complete at \"$BORG_REPO::$archive\"" + '' + ) + cfg.gameserver_baks)} + exit 0 + } + start_time=$(date +%s) + backup + end_time=$(date +%s) + exec_time=$((end_time - start_time)) + cpu_usage=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1"%"}') + echo "" + echo "backup stats:" + echo "exec time: $exec_time" + echo "cpu usage: $cpu_usage" + ''; + }; + }; + # create timer to run backups daily + systemd.timers.gameserver_backups = { + description = "daily borg backup timer"; + wantedBy = ["timers.target"]; + timerConfig = { + OnCalendar = "*-*-* *:00:00"; # every hour, at :01 (one min after db dump) + Persistent = true; + }; + }; + + # db backups + services.mysqlBackup = lib.mkIf config.services.mysql.enable { + # mc servers use this + enable = true; + location = "/var/backup/mysql"; + user = "root"; + calendar = "*-*-* *:59:00"; + compressionAlg = "zstd"; + databases = config.services.mysql.ensureDatabases; # set to all databases defined in esure databases + }; + services.postgresqlBackup = lib.mkIf config.services.postgresql.enable { + # immich uses this + enable = true; + location = "/var/backup/postgresql"; + compression = "zstd"; # optional: "xz", "zstd", "none" + startAt = "03:58"; + databases = ["immich"]; # set to all databases defined in esure databases + #databases = config.services.postgresql.ensureDatabases; # set to all databases defined in esure databases + }; + + # install borg binary + environment.systemPackages = with pkgs; [borgbackup tree]; + + # declare secret for repo password + sops.secrets = { + "borg_passwd" = { + owner = "root"; + group = "root"; + }; + }; + }; +}