From 3eb9c9b402cb6f207afd47c330d6b8252d2d7b21 Mon Sep 17 00:00:00 2001 From: blake Date: Sat, 18 Oct 2025 17:06:33 -0500 Subject: [PATCH] backups shenanagians --- bin/borg_lf.sh | 99 ++++++++++++++++++++ bin/lf_borg.sh | 73 --------------- hosts/nixos/snowbelle/configuration.nix | 2 - modules/homelab/immich/default.nix | 3 + modules/homelab/minecraft_recpro/default.nix | 9 +- modules/system/backups/default.nix | 52 ++-------- 6 files changed, 117 insertions(+), 121 deletions(-) create mode 100755 bin/borg_lf.sh delete mode 100755 bin/lf_borg.sh diff --git a/bin/borg_lf.sh b/bin/borg_lf.sh new file mode 100755 index 0000000..a31b9e1 --- /dev/null +++ b/bin/borg_lf.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +set -euo pipefail + +# --- SUDO CHECK --- +if [ "$EUID" -ne 0 ]; then + echo "This script requires root privileges. Re-running with sudo..." + exec sudo "$0" "$@" +fi + +# --- HANDLE OPTIONS --- +BORG_PASSPHRASE="" +SHOW_FOLDERS_ONLY=false +SHOW_FILES=false # Default to showing files and directories + +while getopts "k:fp" opt; do + case "$opt" in + k) + BORG_PASSPHRASE=$(<"$OPTARG") + if [ -z "$BORG_PASSPHRASE" ]; then + echo "Error: The key file is empty." + exit 1 + fi + echo "Using passphrase from key file: $OPTARG" + ;; + f) + SHOW_FOLDERS_ONLY=true + echo "Only directories will be shown in fzf by default." + SHOW_FILES=false + ;; + p) + SHOW_FILES=true + echo "Both files and directories will be shown in fzf." + ;; + *) + echo "Usage: $0 [-k passphrase_file] [-f] [-p] " + exit 1 + ;; + esac +done +shift $((OPTIND - 1)) + +# --- FALLBACK TO /run/secrets/borg_passwd IF NO KEY FILE --- +if [ -z "$BORG_PASSPHRASE" ]; then + if [ $# -eq 0 ]; then + BORG_PASSPHRASE=$(<"/run/secrets/borg_passwd") + echo "Using passphrase from /run/secrets/borg_passwd" + else + # Prompt user for passphrase if neither -k nor /run/secrets/borg_passwd is available + read -s -p "Enter Borg repository passphrase: " BORG_PASSPHRASE + echo + fi +fi + +export BORG_PASSPHRASE + +# --- DEFAULT REPO --- +REPO="${1:-/holocron/archives/servers/snowbelle}" + +# --- CHECK REQUIRED COMMANDS --- +for cmd in borg fzf find tree cp mkdir; do + command -v "$cmd" >/dev/null || { echo "Error: '$cmd' is required but not installed."; exit 1; } +done + +# --- LIST ARCHIVES (sorted, newest last) --- +mapfile -t archives < <(borg list --format="{archive}{NL}" "$REPO" | sort -r) +if [ ${#archives[@]} -eq 0 ]; then + echo "No archives found in $REPO" + exit 1 +fi + +# --- FZF ARCHIVE SELECT --- +selected=$(printf '%s\n' "${archives[@]}" | fzf --prompt="Select archive: " --height=40% --border) +if [ -z "$selected" ]; then + echo "No archive selected." + exit 1 +fi +echo "Selected archive: $selected" + +# --- GENERATE A UNIQUE, SHORTER MOUNT POINT --- +MOUNT_POINT="/tmp/$(uuidgen | sha256sum | head -c 2)-restore-${selected}" +mkdir -p "$MOUNT_POINT" + +# --- MOUNT ARCHIVE --- +echo "Mounting '$selected' to $MOUNT_POINT..." +borg mount "$REPO::$selected" "$MOUNT_POINT" + +cleanup() { + echo "Unmounting archive..." + borg umount "$MOUNT_DIR" >/dev/null 2>&1 || true + rmdir "$MOUNT_DIR" >/dev/null 2>&1 || true +} +trap cleanup EXIT INT TERM + +if [ ! -d "$MOUNT_POINT" ]; then + echo "Error: mount failed." + exit 1 +fi + +lf $MOUNT_POINT diff --git a/bin/lf_borg.sh b/bin/lf_borg.sh deleted file mode 100755 index 8478910..0000000 --- a/bin/lf_borg.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# --- Configuration --- -DEFAULT_REPO="/holocron/archives/homelab" -SECRET_PATH="/run/secrets/borg_passwd" - -# --- Usage --- -# ./browse-borg-root.sh [optional-path-to-repo] -REPO="${1:-$DEFAULT_REPO}" - -# --- Always escalate to root at start --- -if [[ $EUID -ne 0 ]]; then - exec sudo --preserve-env=BORG_PASSPHRASE,BORG_REPO "$0" "$@" -fi - -# --- Determine passphrase --- -if [[ -z "${BORG_PASSPHRASE:-}" ]]; then - if [[ "$REPO" == /holocron* && -f "$SECRET_PATH" ]]; then - echo "Using default Borg passphrase from $SECRET_PATH" - BORG_PASSPHRASE=$(<"$SECRET_PATH") - else - read -rsp "Enter Borg passphrase: " BORG_PASSPHRASE - echo - fi -fi -export BORG_PASSPHRASE - -# --- Check dependencies --- -for cmd in borg fzf lf; do - if ! command -v "$cmd" &>/dev/null; then - echo "Error: '$cmd' is required but not installed." >&2 - exit 1 - fi -done - -# --- Verify repo exists --- -if [[ ! -d "$REPO" ]]; then - echo "Error: repository path '$REPO' not found." - exit 1 -fi - -# --- List archives (newest on bottom) --- -archives=$(borg list --format "{archive} {time}\n" "$REPO" \ - | sort -k2 \ - | awk '{print $1}') - -if [[ -z "$archives" ]]; then - echo "No archives found in $REPO" - exit 0 -fi - -# --- Select archive with fzf --- -archive=$(echo "$archives" | fzf --reverse --prompt="Select archive: ") -if [[ -z "$archive" ]]; then - echo "No archive selected." - exit 0 -fi - -# --- Mount --- -MOUNT_DIR=$(mktemp -d -t borg-mnt-XXXXXX) -echo "Mounting archive '$archive' at $MOUNT_DIR..." - -cleanup() { - echo "Unmounting archive..." - borg umount "$MOUNT_DIR" >/dev/null 2>&1 || true - rmdir "$MOUNT_DIR" >/dev/null 2>&1 || true -} -trap cleanup EXIT INT TERM - -borg mount "$REPO::$archive" "$MOUNT_DIR" -lf "$MOUNT_DIR" - diff --git a/hosts/nixos/snowbelle/configuration.nix b/hosts/nixos/snowbelle/configuration.nix index 977dbca..da274bb 100644 --- a/hosts/nixos/snowbelle/configuration.nix +++ b/hosts/nixos/snowbelle/configuration.nix @@ -18,8 +18,6 @@ in system = { ssh.enable = true; - backups.enable = true; - backups.repo = "/holocron/archives/servers/snowbelle"; sops.enable = true; podman.enable = true; yubikey.enable = true; diff --git a/modules/homelab/immich/default.nix b/modules/homelab/immich/default.nix index 5bfbc54..16c1c4a 100644 --- a/modules/homelab/immich/default.nix +++ b/modules/homelab/immich/default.nix @@ -107,6 +107,9 @@ in { } ]; + # add postgresql database that is automatically created to the backup list + services.postgresqlBackup.databases = ["immich"]; # set to all databases defined in esure databases + # add to backups homelab.baks = { ${service} = {paths = [cfg.data_dir "/var/lib/redis-immich" "/var/backup/postgresql/immich.sql.zstd"];}; diff --git a/modules/homelab/minecraft_recpro/default.nix b/modules/homelab/minecraft_recpro/default.nix index 7009027..1f7e653 100644 --- a/modules/homelab/minecraft_recpro/default.nix +++ b/modules/homelab/minecraft_recpro/default.nix @@ -10,7 +10,7 @@ servers = { velocity = { data_dir = "/var/lib/gameservers/minecraft_recpro/velocity"; - db_dumb_dir = "/var/backup/mysql/${service}_db.zst"; + db_dump_dir = "/var/backup/mysql/${service}_db.zst"; ram = "2G"; }; smp = { @@ -138,7 +138,12 @@ in { services.borgbackup.jobs.${service} = { archiveBaseName = service; repo = cfg.backup_repo; - paths = lib.flatten (lib.attrValues (lib.mapAttrs (_: srv: [srv.data_dir]) servers)); + #paths = lib.flatten (lib.attrValues (lib.mapAttrs (_: srv: [srv.data_dir]) servers)); + paths = lib.flatten ( + lib.attrValues ( + lib.mapAttrs (_: srv: [srv.data_dir] ++ (if builtins.hasAttr "db_dump_dir" srv then [srv.db_dump_dir] else [])) servers + ) + ); compression = "auto,zstd"; startAt = "*-*-* *:00:00"; group = "archives"; diff --git a/modules/system/backups/default.nix b/modules/system/backups/default.nix index c623181..16d0643 100644 --- a/modules/system/backups/default.nix +++ b/modules/system/backups/default.nix @@ -5,17 +5,7 @@ ... }: /* -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 ]; }; - }; +this module */ let cfg = config.system.backups; @@ -34,59 +24,33 @@ in { 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 != {}) { - # db backups + # mysql backups currently minecraft_recpro is the only thing using this services.mysqlBackup = lib.mkIf config.services.mysql.enable { - # mc servers use this enable = true; location = "/var/backup/mysql"; user = "root"; - calendar = "*-*-* *:59:50"; + calendar = "*-*-* *:59:45"; # goes fast, included in back up with server dirs at **:00 compressionAlg = "zstd"; databases = config.services.mysql.ensureDatabases; # set to all databases defined in esure databases }; + # postgresql backups currently immich is the only user 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:59"; - databases = ["immich"]; # set to all databases defined in esure databases + startAt = "03:59"; # the dump is included in a backup taken at 4:00 + # currently setting this in the immich file + #databases = ["immich"]; # set to all databases defined in esure databases #databases = config.services.postgresql.ensureDatabases; # set to all databases defined in esure databases }; - # helpful + # helpful and for scripts environment.systemPackages = with pkgs; [borgbackup tree]; - # declare secret for repo password - sops.secrets = { - "borg_passwd" = { - owner = "root"; - group = "root"; - }; - }; }; }