1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 cfg = config.services.postgresqlBackup;
8
9 postgresqlBackupService = db: dumpCmd:
10 let
11 compressSuffixes = {
12 "none" = "";
13 "gzip" = ".gz";
14 "zstd" = ".zstd";
15 };
16 compressSuffix = getAttr cfg.compression compressSuffixes;
17
18 compressCmd = getAttr cfg.compression {
19 "none" = "cat";
20 "gzip" = "${pkgs.gzip}/bin/gzip -c";
21 "zstd" = "${pkgs.zstd}/bin/zstd -c";
22 };
23
24 mkSqlPath = prefix: suffix: "${cfg.location}/${db}${prefix}.sql${suffix}";
25 curFile = mkSqlPath "" compressSuffix;
26 prevFile = mkSqlPath ".prev" compressSuffix;
27 prevFiles = map (mkSqlPath ".prev") (attrValues compressSuffixes);
28 inProgressFile = mkSqlPath ".in-progress" compressSuffix;
29 in {
30 enable = true;
31
32 description = "Backup of ${db} database(s)";
33
34 requires = [ "postgresql.service" ];
35
36 path = [ pkgs.coreutils config.services.postgresql.package ];
37
38 script = ''
39 set -e -o pipefail
40
41 umask 0077 # ensure backup is only readable by postgres user
42
43 if [ -e ${curFile} ]; then
44 rm -f ${toString prevFiles}
45 mv ${curFile} ${prevFile}
46 fi
47
48 ${dumpCmd} \
49 | ${compressCmd} \
50 > ${inProgressFile}
51
52 mv ${inProgressFile} ${curFile}
53 '';
54
55 serviceConfig = {
56 Type = "oneshot";
57 User = "postgres";
58 };
59
60 startAt = cfg.startAt;
61 };
62
63in {
64
65 imports = [
66 (mkRemovedOptionModule [ "services" "postgresqlBackup" "period" ] ''
67 A systemd timer is now used instead of cron.
68 The starting time can be configured via <literal>services.postgresqlBackup.startAt</literal>.
69 '')
70 ];
71
72 options = {
73 services.postgresqlBackup = {
74 enable = mkEnableOption "PostgreSQL dumps";
75
76 startAt = mkOption {
77 default = "*-*-* 01:15:00";
78 type = with types; either (listOf str) str;
79 description = ''
80 This option defines (see <literal>systemd.time</literal> for format) when the
81 databases should be dumped.
82 The default is to update at 01:15 (at night) every day.
83 '';
84 };
85
86 backupAll = mkOption {
87 default = cfg.databases == [];
88 defaultText = literalExpression "services.postgresqlBackup.databases == []";
89 type = lib.types.bool;
90 description = ''
91 Backup all databases using pg_dumpall.
92 This option is mutual exclusive to
93 <literal>services.postgresqlBackup.databases</literal>.
94 The resulting backup dump will have the name all.sql.gz.
95 This option is the default if no databases are specified.
96 '';
97 };
98
99 databases = mkOption {
100 default = [];
101 type = types.listOf types.str;
102 description = ''
103 List of database names to dump.
104 '';
105 };
106
107 location = mkOption {
108 default = "/var/backup/postgresql";
109 type = types.path;
110 description = ''
111 Path of directory where the PostgreSQL database dumps will be placed.
112 '';
113 };
114
115 pgdumpOptions = mkOption {
116 type = types.separatedString " ";
117 default = "-C";
118 description = ''
119 Command line options for pg_dump. This options is not used
120 if <literal>config.services.postgresqlBackup.backupAll</literal> is enabled.
121 Note that config.services.postgresqlBackup.backupAll is also active,
122 when no databases where specified.
123 '';
124 };
125
126 compression = mkOption {
127 type = types.enum ["none" "gzip" "zstd"];
128 default = "gzip";
129 description = ''
130 The type of compression to use on the generated database dump.
131 '';
132 };
133 };
134
135 };
136
137 config = mkMerge [
138 {
139 assertions = [{
140 assertion = cfg.backupAll -> cfg.databases == [];
141 message = "config.services.postgresqlBackup.backupAll cannot be used together with config.services.postgresqlBackup.databases";
142 }];
143 }
144 (mkIf cfg.enable {
145 systemd.tmpfiles.rules = [
146 "d '${cfg.location}' 0700 postgres - - -"
147 ];
148 })
149 (mkIf (cfg.enable && cfg.backupAll) {
150 systemd.services.postgresqlBackup =
151 postgresqlBackupService "all" "pg_dumpall";
152 })
153 (mkIf (cfg.enable && !cfg.backupAll) {
154 systemd.services = listToAttrs (map (db:
155 let
156 cmd = "pg_dump ${cfg.pgdumpOptions} ${db}";
157 in {
158 name = "postgresqlBackup-${db}";
159 value = postgresqlBackupService db cmd;
160 }) cfg.databases);
161 })
162 ];
163
164}