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