1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 inherit (pkgs) mariadb gzip;
8
9 cfg = config.services.mysqlBackup;
10 defaultUser = "mysqlbackup";
11
12 backupScript = ''
13 set -o pipefail
14 failed=""
15 ${concatMapStringsSep "\n" backupDatabaseScript cfg.databases}
16 if [ -n "$failed" ]; then
17 echo "Backup of database(s) failed:$failed"
18 exit 1
19 fi
20 '';
21 backupDatabaseScript = db: ''
22 dest="${cfg.location}/${db}.gz"
23 if ${mariadb}/bin/mysqldump ${optionalString cfg.singleTransaction "--single-transaction"} ${db} | ${gzip}/bin/gzip -c > $dest.tmp; then
24 mv $dest.tmp $dest
25 echo "Backed up to $dest"
26 else
27 echo "Failed to back up to $dest"
28 rm -f $dest.tmp
29 failed="$failed ${db}"
30 fi
31 '';
32
33in
34
35{
36 options = {
37
38 services.mysqlBackup = {
39
40 enable = mkEnableOption (lib.mdDoc "MySQL backups");
41
42 calendar = mkOption {
43 type = types.str;
44 default = "01:15:00";
45 description = lib.mdDoc ''
46 Configured when to run the backup service systemd unit (DayOfWeek Year-Month-Day Hour:Minute:Second).
47 '';
48 };
49
50 user = mkOption {
51 type = types.str;
52 default = defaultUser;
53 description = lib.mdDoc ''
54 User to be used to perform backup.
55 '';
56 };
57
58 databases = mkOption {
59 default = [];
60 type = types.listOf types.str;
61 description = lib.mdDoc ''
62 List of database names to dump.
63 '';
64 };
65
66 location = mkOption {
67 type = types.path;
68 default = "/var/backup/mysql";
69 description = lib.mdDoc ''
70 Location to put the gzipped MySQL database dumps.
71 '';
72 };
73
74 singleTransaction = mkOption {
75 default = false;
76 type = types.bool;
77 description = lib.mdDoc ''
78 Whether to create database dump in a single transaction
79 '';
80 };
81 };
82
83 };
84
85 config = mkIf cfg.enable {
86 users.users = optionalAttrs (cfg.user == defaultUser) {
87 ${defaultUser} = {
88 isSystemUser = true;
89 createHome = false;
90 home = cfg.location;
91 group = "nogroup";
92 };
93 };
94
95 services.mysql.ensureUsers = [{
96 name = cfg.user;
97 ensurePermissions = with lib;
98 let
99 privs = "SELECT, SHOW VIEW, TRIGGER, LOCK TABLES";
100 grant = db: nameValuePair "${db}.*" privs;
101 in
102 listToAttrs (map grant cfg.databases);
103 }];
104
105 systemd = {
106 timers.mysql-backup = {
107 description = "Mysql backup timer";
108 wantedBy = [ "timers.target" ];
109 timerConfig = {
110 OnCalendar = cfg.calendar;
111 AccuracySec = "5m";
112 Unit = "mysql-backup.service";
113 };
114 };
115 services.mysql-backup = {
116 description = "MySQL backup service";
117 enable = true;
118 serviceConfig = {
119 Type = "oneshot";
120 User = cfg.user;
121 };
122 script = backupScript;
123 };
124 tmpfiles.rules = [
125 "d ${cfg.location} 0700 ${cfg.user} - - -"
126 ];
127 };
128 };
129
130}