From 60d89ca103d8e5ed000d4949e0714a638bec4fbe Mon Sep 17 00:00:00 2001 From: Christoph Schmatzler Date: Sat, 20 Dec 2025 21:31:08 +0000 Subject: [PATCH] refactor --- hosts/mindy/default.nix | 117 +--------------------- modules/pgbackrest.nix | 216 ++++++++++++++++++++++++++++++++++++++++ profiles/postgresql.nix | 8 +- 3 files changed, 225 insertions(+), 116 deletions(-) create mode 100644 modules/pgbackrest.nix diff --git a/hosts/mindy/default.nix b/hosts/mindy/default.nix index 55ef88c..60321c9 100644 --- a/hosts/mindy/default.nix +++ b/hosts/mindy/default.nix @@ -1,10 +1,7 @@ { - config, - lib, modulesPath, hostname, inputs, - pkgs, user, constants, ... @@ -30,118 +27,12 @@ group = "postgres"; }; - environment.systemPackages = [ - pkgs.pgbackrest - (pkgs.writeShellScriptBin "pgbackrest-archive-push" '' - set -a - source /run/secrets/mindy-pgbackrest - set +a - exec ${pkgs.pgbackrest}/bin/pgbackrest --stanza=main archive-push "$1" - '') - ]; - - services.postgresql.settings.archive_command = lib.mkForce "${pkgs.writeShellScript "pgbackrest-archive-push" '' - set -a - source /run/secrets/mindy-pgbackrest - set +a - exec ${pkgs.pgbackrest}/bin/pgbackrest --stanza=main archive-push "$1" - ''} %p"; - - environment.etc."pgbackrest/pgbackrest.conf".text = '' - [global] - repo1-type=s3 - repo1-s3-endpoint=s3.eu-central-003.backblazeb2.com - repo1-s3-bucket=mindy-pgbackrest - repo1-s3-region=eu-central-003 - repo1-path=/backups - repo1-retention-full=7 - repo1-retention-diff=7 - repo1-cipher-type=aes-256-cbc - compress-type=zst - compress-level=3 - process-max=2 - log-level-console=info - log-level-file=detail - log-path=/var/log/pgbackrest - spool-path=/var/spool/pgbackrest - - [main] - pg1-path=/var/lib/postgresql/${config.services.postgresql.package.psqlSchema} - pg1-user=postgres - ''; - - systemd.services.pgbackrest-stanza-create = { - description = "pgBackRest Stanza Create"; - after = ["postgresql.service"]; - requires = ["postgresql.service"]; - path = [pkgs.pgbackrest]; - serviceConfig = { - Type = "oneshot"; - User = "postgres"; - EnvironmentFile = "/run/secrets/mindy-pgbackrest"; - RemainAfterExit = true; - }; - script = '' - pgbackrest --stanza=main stanza-create || true - ''; + services.pgbackrest = { + enable = true; + secretFile = "/run/secrets/mindy-pgbackrest"; + s3.bucket = "mindy-pgbackrest"; }; - systemd.services.pgbackrest-backup = { - description = "pgBackRest Full Backup"; - after = ["postgresql.service" "pgbackrest-stanza-create.service"]; - requires = ["postgresql.service"]; - wants = ["pgbackrest-stanza-create.service"]; - path = [pkgs.pgbackrest]; - serviceConfig = { - Type = "oneshot"; - User = "postgres"; - EnvironmentFile = "/run/secrets/mindy-pgbackrest"; - }; - script = '' - pgbackrest --stanza=main backup --type=full - ''; - }; - - systemd.timers.pgbackrest-backup = { - wantedBy = ["timers.target"]; - timerConfig = { - OnCalendar = "daily"; - Persistent = true; - RandomizedDelaySec = "1h"; - }; - }; - - systemd.services.pgbackrest-backup-diff = { - description = "pgBackRest Differential Backup"; - after = ["postgresql.service" "pgbackrest-stanza-create.service"]; - requires = ["postgresql.service"]; - wants = ["pgbackrest-stanza-create.service"]; - path = [pkgs.pgbackrest]; - serviceConfig = { - Type = "oneshot"; - User = "postgres"; - EnvironmentFile = "/run/secrets/mindy-pgbackrest"; - }; - script = '' - pgbackrest --stanza=main backup --type=diff - ''; - }; - - systemd.timers.pgbackrest-backup-diff = { - wantedBy = ["timers.target"]; - timerConfig = { - OnCalendar = "hourly"; - Persistent = true; - RandomizedDelaySec = "5m"; - }; - }; - - systemd.tmpfiles.rules = [ - "d /var/lib/pgbackrest 0750 postgres postgres -" - "d /var/log/pgbackrest 0750 postgres postgres -" - "d /var/spool/pgbackrest 0750 postgres postgres -" - ]; - home-manager.users.${user} = { pkgs, lib, diff --git a/modules/pgbackrest.nix b/modules/pgbackrest.nix new file mode 100644 index 0000000..9bf0ca2 --- /dev/null +++ b/modules/pgbackrest.nix @@ -0,0 +1,216 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.services.pgbackrest; +in { + options.services.pgbackrest = { + enable = mkEnableOption "pgBackRest PostgreSQL backup"; + + stanza = mkOption { + type = types.str; + default = "main"; + description = "Name of the pgBackRest stanza"; + }; + + secretFile = mkOption { + type = types.path; + description = "Path to the environment file containing S3 credentials and cipher passphrase"; + }; + + s3 = { + endpoint = mkOption { + type = types.str; + default = "s3.eu-central-003.backblazeb2.com"; + description = "S3 endpoint URL"; + }; + + bucket = mkOption { + type = types.str; + description = "S3 bucket name"; + }; + + region = mkOption { + type = types.str; + default = "eu-central-003"; + description = "S3 region"; + }; + + path = mkOption { + type = types.str; + default = "/backups"; + description = "Path within the S3 bucket"; + }; + }; + + retention = { + full = mkOption { + type = types.int; + default = 7; + description = "Number of full backups to retain"; + }; + + diff = mkOption { + type = types.int; + default = 7; + description = "Number of differential backups to retain"; + }; + }; + + compression = { + type = mkOption { + type = types.str; + default = "zst"; + description = "Compression algorithm (none, gz, lz4, zst)"; + }; + + level = mkOption { + type = types.int; + default = 3; + description = "Compression level"; + }; + }; + + processMax = mkOption { + type = types.int; + default = 2; + description = "Maximum number of processes for parallel operations"; + }; + + schedule = { + full = mkOption { + type = types.str; + default = "daily"; + description = "OnCalendar expression for full backups"; + }; + + diff = mkOption { + type = types.str; + default = "hourly"; + description = "OnCalendar expression for differential backups"; + }; + }; + }; + + config = mkIf cfg.enable (let + archivePushScript = pkgs.writeShellScript "pgbackrest-archive-push" '' + set -a + source ${cfg.secretFile} + set +a + exec ${pkgs.pgbackrest}/bin/pgbackrest --stanza=${cfg.stanza} archive-push "$1" + ''; + in { + environment.systemPackages = [ + pkgs.pgbackrest + (pkgs.writeShellScriptBin "pgbackrest-wrapper" '' + set -a + source ${cfg.secretFile} + set +a + exec ${pkgs.pgbackrest}/bin/pgbackrest "$@" + '') + ]; + + services.postgresql.settings = { + archive_mode = "on"; + archive_command = "${archivePushScript} %p"; + }; + + environment.etc."pgbackrest/pgbackrest.conf".text = '' + [global] + repo1-type=s3 + repo1-s3-endpoint=${cfg.s3.endpoint} + repo1-s3-bucket=${cfg.s3.bucket} + repo1-s3-region=${cfg.s3.region} + repo1-path=${cfg.s3.path} + repo1-retention-full=${toString cfg.retention.full} + repo1-retention-diff=${toString cfg.retention.diff} + repo1-cipher-type=aes-256-cbc + compress-type=${cfg.compression.type} + compress-level=${toString cfg.compression.level} + process-max=${toString cfg.processMax} + log-level-console=info + log-level-file=detail + log-path=/var/log/pgbackrest + spool-path=/var/spool/pgbackrest + + [${cfg.stanza}] + pg1-path=/var/lib/postgresql/${config.services.postgresql.package.psqlSchema} + pg1-user=postgres + ''; + + systemd.services.pgbackrest-stanza-create = { + description = "pgBackRest Stanza Create"; + after = ["postgresql.service"]; + requires = ["postgresql.service"]; + path = [pkgs.pgbackrest]; + serviceConfig = { + Type = "oneshot"; + User = "postgres"; + EnvironmentFile = cfg.secretFile; + RemainAfterExit = true; + }; + script = '' + pgbackrest --stanza=${cfg.stanza} stanza-create || true + ''; + }; + + systemd.services.pgbackrest-backup = { + description = "pgBackRest Full Backup"; + after = ["postgresql.service" "pgbackrest-stanza-create.service"]; + requires = ["postgresql.service"]; + wants = ["pgbackrest-stanza-create.service"]; + path = [pkgs.pgbackrest]; + serviceConfig = { + Type = "oneshot"; + User = "postgres"; + EnvironmentFile = cfg.secretFile; + }; + script = '' + pgbackrest --stanza=${cfg.stanza} backup --type=full + ''; + }; + + systemd.timers.pgbackrest-backup = { + wantedBy = ["timers.target"]; + timerConfig = { + OnCalendar = cfg.schedule.full; + Persistent = true; + RandomizedDelaySec = "1h"; + }; + }; + + systemd.services.pgbackrest-backup-diff = { + description = "pgBackRest Differential Backup"; + after = ["postgresql.service" "pgbackrest-stanza-create.service"]; + requires = ["postgresql.service"]; + wants = ["pgbackrest-stanza-create.service"]; + path = [pkgs.pgbackrest]; + serviceConfig = { + Type = "oneshot"; + User = "postgres"; + EnvironmentFile = cfg.secretFile; + }; + script = '' + pgbackrest --stanza=${cfg.stanza} backup --type=diff + ''; + }; + + systemd.timers.pgbackrest-backup-diff = { + wantedBy = ["timers.target"]; + timerConfig = { + OnCalendar = cfg.schedule.diff; + Persistent = true; + RandomizedDelaySec = "5m"; + }; + }; + + systemd.tmpfiles.rules = [ + "d /var/lib/pgbackrest 0750 postgres postgres -" + "d /var/log/pgbackrest 0750 postgres postgres -" + "d /var/spool/pgbackrest 0750 postgres postgres -" + ]; + }); +} diff --git a/profiles/postgresql.nix b/profiles/postgresql.nix index 7919b32..ab49ac9 100644 --- a/profiles/postgresql.nix +++ b/profiles/postgresql.nix @@ -1,4 +1,8 @@ -{lib, pkgs, ...}: { +{ + lib, + pkgs, + ... +}: { services.postgresql = { enable = true; package = pkgs.postgresql_18; @@ -6,8 +10,6 @@ settings = { listen_addresses = lib.mkForce "*"; wal_level = "logical"; - archive_mode = "on"; - archive_command = "${pkgs.pgbackrest}/bin/pgbackrest --stanza=main archive-push %p"; max_wal_senders = 3; max_connections = 100; shared_buffers = "256MB";