From 9cb10a96fd14400871e71aff8b30c6220c4ef369 Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Mon, 17 Aug 2026 20:49:30 +0300 Subject: [PATCH] configure borg production backups --- nix/nixos-module.nix | 130 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 115 insertions(+), 15 deletions(-) diff --git a/nix/nixos-module.nix b/nix/nixos-module.nix index 8bfb03a..30f8ddf 100644 --- a/nix/nixos-module.nix +++ b/nix/nixos-module.nix @@ -16,9 +16,25 @@ let uploadLimits = import ./upload-limits.nix; storagePath = "${cfg.stateDir}/storage"; cachePath = "${cfg.cacheDir}/bootstrap-cache"; + backupDir = "/var/backup/rabbi-gerzi"; + databaseDumpPath = "${backupDir}/rabbi-gerzi.dump"; + phpFpmWasActivePath = "/root/.cache/borg/rabbi-gerzi-phpfpm-was-active"; setupService = "rabbi-gerzi-setup"; phpfpmService = "phpfpm-${poolName}"; + databaseDumpCommand = lib.concatStringsSep " " [ + "${config.services.postgresql.package}/bin/pg_dump" + "--format=custom" + "--dbname=${cfg.database.name}" + ]; + + secretFileOption = + description: + lib.mkOption { + type = lib.types.path; + inherit description; + }; + appEnvironment = { APP_ENV = "production"; APP_DEBUG = "false"; @@ -55,6 +71,11 @@ in options.services.rabbi-gerzi = { enable = lib.mkEnableOption "the Rabbi Gerzi application"; + secretFiles = { + borgPassphrase = secretFileOption "Borg repository passphrase file."; + borgPrivateKey = secretFileOption "Borg repository SSH private key file."; + }; + user = lib.mkOption { type = lib.types.str; default = "rabbi-gerzi"; @@ -221,21 +242,85 @@ in ]; }; - systemd.tmpfiles.rules = map makeDirectoryRule [ - cfg.stateDir - cfg.cacheDir - storagePath - "${storagePath}/app" - "${storagePath}/app/private" - "${storagePath}/app/public" - "${storagePath}/framework" - "${storagePath}/framework/cache" - "${storagePath}/framework/cache/data" - "${storagePath}/framework/sessions" - "${storagePath}/framework/views" - "${storagePath}/logs" - cachePath - ]; + services.borgbackup.jobs.rabbi-gerzi = { + paths = [ + cfg.stateDir + databaseDumpPath + ]; + repo = "ssh://mgjjruz9@mgjjruz9.repo.borgbase.com/./repo"; + user = "root"; + group = "root"; + encryption = { + mode = "repokey-blake2"; + passCommand = "${pkgs.coreutils}/bin/cat ${cfg.secretFiles.borgPassphrase}"; + }; + doInit = true; + compression = "auto,zstd"; + startAt = "*-*-* 05:15:00"; + persistentTimer = true; + prune.keep = { + daily = 7; + weekly = 4; + monthly = 6; + }; + readWritePaths = [ backupDir ]; + environment.BORG_RSH = lib.concatStringsSep " " [ + "ssh" + "-i ${cfg.secretFiles.borgPrivateKey}" + "-o IdentitiesOnly=yes" + "-o BatchMode=yes" + "-o StrictHostKeyChecking=accept-new" + "-o UserKnownHostsFile=/root/.config/borg/known_hosts" + ]; + preHook = '' + phpFpmWasActive=${phpFpmWasActivePath} + dumpFile=${databaseDumpPath} + + if ${pkgs.systemd}/bin/systemctl is-active --quiet ${phpfpmService}.service; then + touch "$phpFpmWasActive" + ${pkgs.systemd}/bin/systemctl stop ${phpfpmService}.service + else + rm -f "$phpFpmWasActive" + fi + + rm -f "$dumpFile" + umask 077 + ${lib.getExe' pkgs.su "su"} \ + -s ${pkgs.runtimeShell} \ + ${config.services.postgresql.superUser} \ + -c '${databaseDumpCommand}' \ + > "$dumpFile" + ''; + postHook = '' + phpFpmWasActive=${phpFpmWasActivePath} + dumpFile=${databaseDumpPath} + + rm -f "$dumpFile" + + if [ -e "$phpFpmWasActive" ]; then + ${pkgs.systemd}/bin/systemctl start ${phpfpmService}.service + rm -f "$phpFpmWasActive" + fi + ''; + }; + + systemd.tmpfiles.rules = + (map makeDirectoryRule [ + cfg.stateDir + cfg.cacheDir + storagePath + "${storagePath}/app" + "${storagePath}/app/private" + "${storagePath}/app/public" + "${storagePath}/framework" + "${storagePath}/framework/cache" + "${storagePath}/framework/cache/data" + "${storagePath}/framework/sessions" + "${storagePath}/framework/views" + "${storagePath}/logs" + cachePath + ]) + ++ [ "d ${backupDir} 0700 root root - -" ]; services.phpfpm.pools.${poolName} = { inherit (cfg) user group; @@ -289,6 +374,7 @@ in environment = appEnvironment; serviceConfig = { EnvironmentFile = cfg.backend.environmentFile; + KillSignal = "SIGQUIT"; ReadWritePaths = [ cfg.stateDir cfg.cacheDir @@ -296,6 +382,20 @@ in }; }; + systemd.services.borgbackup-job-rabbi-gerzi = { + after = [ + "sops-install-secrets.service" + "network-online.target" + "postgresql.service" + "${setupService}.service" + ]; + wants = [ + "network-online.target" + "postgresql.service" + "${setupService}.service" + ]; + }; + services.nginx = { enable = lib.mkDefault true; recommendedGzipSettings = lib.mkDefault true;