summary refs log tree commit diff
path: root/nixos/modules/services/backup/postgresql-backup.nix
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/services/backup/postgresql-backup.nix')
-rw-r--r--nixos/modules/services/backup/postgresql-backup.nix164
1 files changed, 164 insertions, 0 deletions
diff --git a/nixos/modules/services/backup/postgresql-backup.nix b/nixos/modules/services/backup/postgresql-backup.nix
new file mode 100644
index 00000000000..562458eb457
--- /dev/null
+++ b/nixos/modules/services/backup/postgresql-backup.nix
@@ -0,0 +1,164 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.postgresqlBackup;
+
+  postgresqlBackupService = db: dumpCmd:
+    let
+      compressSuffixes = {
+        "none" = "";
+        "gzip" = ".gz";
+        "zstd" = ".zstd";
+      };
+      compressSuffix = getAttr cfg.compression compressSuffixes;
+
+      compressCmd = getAttr cfg.compression {
+        "none" = "cat";
+        "gzip" = "${pkgs.gzip}/bin/gzip -c";
+        "zstd" = "${pkgs.zstd}/bin/zstd -c";
+      };
+
+      mkSqlPath = prefix: suffix: "${cfg.location}/${db}${prefix}.sql${suffix}";
+      curFile = mkSqlPath "" compressSuffix;
+      prevFile = mkSqlPath ".prev" compressSuffix;
+      prevFiles = map (mkSqlPath ".prev") (attrValues compressSuffixes);
+      inProgressFile = mkSqlPath ".in-progress" compressSuffix;
+    in {
+      enable = true;
+
+      description = "Backup of ${db} database(s)";
+
+      requires = [ "postgresql.service" ];
+
+      path = [ pkgs.coreutils config.services.postgresql.package ];
+
+      script = ''
+        set -e -o pipefail
+
+        umask 0077 # ensure backup is only readable by postgres user
+
+        if [ -e ${curFile} ]; then
+          rm -f ${toString prevFiles}
+          mv ${curFile} ${prevFile}
+        fi
+
+        ${dumpCmd} \
+          | ${compressCmd} \
+          > ${inProgressFile}
+
+        mv ${inProgressFile} ${curFile}
+      '';
+
+      serviceConfig = {
+        Type = "oneshot";
+        User = "postgres";
+      };
+
+      startAt = cfg.startAt;
+    };
+
+in {
+
+  imports = [
+    (mkRemovedOptionModule [ "services" "postgresqlBackup" "period" ] ''
+       A systemd timer is now used instead of cron.
+       The starting time can be configured via <literal>services.postgresqlBackup.startAt</literal>.
+    '')
+  ];
+
+  options = {
+    services.postgresqlBackup = {
+      enable = mkEnableOption "PostgreSQL dumps";
+
+      startAt = mkOption {
+        default = "*-*-* 01:15:00";
+        type = with types; either (listOf str) str;
+        description = ''
+          This option defines (see <literal>systemd.time</literal> for format) when the
+          databases should be dumped.
+          The default is to update at 01:15 (at night) every day.
+        '';
+      };
+
+      backupAll = mkOption {
+        default = cfg.databases == [];
+        defaultText = literalExpression "services.postgresqlBackup.databases == []";
+        type = lib.types.bool;
+        description = ''
+          Backup all databases using pg_dumpall.
+          This option is mutual exclusive to
+          <literal>services.postgresqlBackup.databases</literal>.
+          The resulting backup dump will have the name all.sql.gz.
+          This option is the default if no databases are specified.
+        '';
+      };
+
+      databases = mkOption {
+        default = [];
+        type = types.listOf types.str;
+        description = ''
+          List of database names to dump.
+        '';
+      };
+
+      location = mkOption {
+        default = "/var/backup/postgresql";
+        type = types.path;
+        description = ''
+          Path of directory where the PostgreSQL database dumps will be placed.
+        '';
+      };
+
+      pgdumpOptions = mkOption {
+        type = types.separatedString " ";
+        default = "-C";
+        description = ''
+          Command line options for pg_dump. This options is not used
+          if <literal>config.services.postgresqlBackup.backupAll</literal> is enabled.
+          Note that config.services.postgresqlBackup.backupAll is also active,
+          when no databases where specified.
+        '';
+      };
+
+      compression = mkOption {
+        type = types.enum ["none" "gzip" "zstd"];
+        default = "gzip";
+        description = ''
+          The type of compression to use on the generated database dump.
+        '';
+      };
+    };
+
+  };
+
+  config = mkMerge [
+    {
+      assertions = [{
+        assertion = cfg.backupAll -> cfg.databases == [];
+        message = "config.services.postgresqlBackup.backupAll cannot be used together with config.services.postgresqlBackup.databases";
+      }];
+    }
+    (mkIf cfg.enable {
+      systemd.tmpfiles.rules = [
+        "d '${cfg.location}' 0700 postgres - - -"
+      ];
+    })
+    (mkIf (cfg.enable && cfg.backupAll) {
+      systemd.services.postgresqlBackup =
+        postgresqlBackupService "all" "pg_dumpall";
+    })
+    (mkIf (cfg.enable && !cfg.backupAll) {
+      systemd.services = listToAttrs (map (db:
+        let
+          cmd = "pg_dump ${cfg.pgdumpOptions} ${db}";
+        in {
+          name = "postgresqlBackup-${db}";
+          value = postgresqlBackupService db cmd;
+        }) cfg.databases);
+    })
+  ];
+
+}