{ config, pkgs, lib, utils, ... }: with utils; with lib; let cfg = config.services.pgbackrest; settingsFormat = pkgs.formats.ini { listsAsDuplicateKeys = true; mkKeyValue = lib.generators.mkKeyValueDefault { mkValueString = v: with builtins; let err = t: v: abort ("mkValueString: " + "${t} not supported: ${toPretty {} v}"); in if isInt v then toString v # convert derivations to store paths else if lib.isDerivation v then toString v # we default to not quoting strings else if isString v then v # isString returns "1", which is not a good default else if true == v then "y" # here it returns to "", which is even less of a good default else if false == v then "n" else if null == v then "null" # if you have lists you probably want to replace this else if isList v then err "lists" v # same as for lists, might want to replace else if isAttrs v then err "attrsets" v # functions can’t be printed of course else if isFunction v then err "functions" v # Floats currently can't be converted to precise strings, # condition warning on nix version once this isn't a problem anymore # See https://github.com/NixOS/nix/pull/3480 else if isFloat v then libStr.floatToString v else err "this value is" (toString v); } "="; }; loglevelType = types.enum ["off" "error" "warn" "info" "detail" "debug" "trace"]; inherit (utils.systemdUtils.unitOptions) unitOption; in { options = { services.pgbackrest = { enable = mkEnableOption "pgBackRest"; package = mkPackageOption pkgs "pgbackrest" {}; configurePostgresql = { enable = mkEnableOption "configuring PostgreSQL for sending WAL to pgBackRest" // { default = config.services.postgresql.enable; defaultText = literalExpression "config.systemd.services.postgresql.enable"; }; stanza = mkOption { type = types.str; default = config.networking.hostName; }; }; settings = mkOption { type = types.submodule { freeformType = settingsFormat.type; options = { global.log-level-console = mkOption { type = loglevelType; default = "detail"; }; global.log-level-file = mkOption { type = loglevelType; default = "off"; }; global.log-level-stderr = mkOption { type = loglevelType; default = "warn"; }; global.log-subprocess = mkOption { type = types.bool; default = true; }; global.log-timestamp = mkOption { type = types.bool; default = false; }; }; }; default = {}; description = '' Configuration for pgBackRest ''; }; tlsServer = { enable = mkEnableOption "pgBackRest TLS Server"; user = mkOption { type = types.str; default = "postgres"; }; group = mkOption { type = types.str; default = "postgres"; }; }; backups = mkOption { type = types.attrsOf (types.submodule ({ name, ... }: { options = { type = mkOption { type = types.enum ["full" "incr" "diff"]; default = "full"; }; stanza = mkOption { type = types.str; default = cfg.configurePostgresql.stanza; }; repo = mkOption { type = types.nullOr (types.strMatching "^[0-9]+$"); }; user = mkOption { type = types.str; default = "postgres"; }; group = mkOption { type = types.str; default = "postgres"; }; timerConfig = mkOption { type = types.attrsOf unitOption; }; }; })); default = {}; }; }; }; config = mkIf cfg.enable { environment.systemPackages = [ cfg.package ]; services.postgresql.settings = mkIf cfg.configurePostgresql.enable { archive_command = "pgbackrest --stanza ${escapeSystemdExecArg cfg.configurePostgresql.stanza} archive-push %p"; archive_mode = true; max_wal_senders = mkDefault 3; wal_level = "replica"; }; systemd.services = { postgresql.path = mkIf cfg.configurePostgresql.enable [ cfg.package ]; pgbackrest-tls-server = mkIf cfg.tlsServer.enable { description = "pgBackRest TLS-Server"; wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; restartTriggers = [ config.environment.etc."pgbackrest/pgbackrest.conf".source ]; unitConfig = { StartLimitIntervalSec = 0; }; serviceConfig = { Type = "simple"; Restart = "always"; RestartSec = 1; User = cfg.tlsServer.user; Group = cfg.tlsServer.group; ExecStart = "${cfg.package}/bin/pgbackrest server"; ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; }; }; } // mapAttrs' (name: backupCfg: nameValuePair "pgbackrest-backup@${escapeSystemdPath name}" { description = "Perform pgBackRest Backup (${name}${optionalString (!(isNull backupCfg.repo)) " repo${backupCfg.repo}"})"; serviceConfig = { Type = "oneshot"; ExecStart = "${cfg.package}/bin/pgbackrest --type ${escapeSystemdExecArg backupCfg.type} --stanza ${escapeSystemdExecArg backupCfg.stanza}${optionalString (!(isNull backupCfg.repo)) " --repo ${backupCfg.repo}"} backup"; User = backupCfg.user; Group = backupCfg.group; }; }) cfg.backups; systemd.timers = mapAttrs' (name: backupCfg: nameValuePair "pgbackrest-backup@${escapeSystemdPath name}" { wantedBy = [ "timers.target" ]; inherit (backupCfg) timerConfig; }) cfg.backups; environment.etc."pgbackrest/pgbackrest.conf".source = settingsFormat.generate "pgbackrest.conf" cfg.settings; }; }