summary refs log tree commit diff
path: root/nixos/modules/services/backup/mysql-backup.nix
blob: f0c273ffebf154d5841a472a4edcfcadf4f8c8b8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
{ config, lib, pkgs, ... }:

with lib;

let

  inherit (pkgs) mysql gzip;

  cfg = config.services.mysqlBackup;
  defaultUser = "mysqlbackup";

  backupScript = ''
    set -o pipefail
    failed=""
    ${concatMapStringsSep "\n" backupDatabaseScript cfg.databases}
    if [ -n "$failed" ]; then
      echo "Backup of database(s) failed:$failed"
      exit 1
    fi
  '';
  backupDatabaseScript = db: ''
    dest="${cfg.location}/${db}.gz"
    if ${mysql}/bin/mysqldump ${if cfg.singleTransaction then "--single-transaction" else ""} ${db} | ${gzip}/bin/gzip -c > $dest.tmp; then
      mv $dest.tmp $dest
      echo "Backed up to $dest"
    else
      echo "Failed to back up to $dest"
      rm -f $dest.tmp
      failed="$failed ${db}"
    fi
  '';

in

{
  options = {

    services.mysqlBackup = {

      enable = mkOption {
        default = false;
        description = ''
          Whether to enable MySQL backups.
        '';
      };

      calendar = mkOption {
        type = types.str;
        default = "01:15:00";
        description = ''
          Configured when to run the backup service systemd unit (DayOfWeek Year-Month-Day Hour:Minute:Second).
        '';
      };

      user = mkOption {
        default = defaultUser;
        description = ''
          User to be used to perform backup.
        '';
      };

      databases = mkOption {
        default = [];
        description = ''
          List of database names to dump.
        '';
      };

      location = mkOption {
        default = "/var/backup/mysql";
        description = ''
          Location to put the gzipped MySQL database dumps.
        '';
      };

      singleTransaction = mkOption {
        default = false;
        description = ''
          Whether to create database dump in a single transaction
        '';
      };
    };

  };

  config = mkIf cfg.enable {
    users.users = optionalAttrs (cfg.user == defaultUser) (singleton
      { name = defaultUser;
        isSystemUser = true;
        createHome = false;
        home = cfg.location;
        group = "nogroup";
      });

    services.mysql.ensureUsers = [{
      name = cfg.user;
      ensurePermissions = with lib;
        let
          privs = "SELECT, SHOW VIEW, TRIGGER, LOCK TABLES";
          grant = db: nameValuePair "${db}.*" privs;
        in
          listToAttrs (map grant cfg.databases);
    }];

    systemd = {
      timers."mysql-backup" = {
        description = "Mysql backup timer";
        wantedBy = [ "timers.target" ];
        timerConfig = {
          OnCalendar = cfg.calendar;
          AccuracySec = "5m";
          Unit = "mysql-backup.service";
        };
      };
      services."mysql-backup" = {
        description = "Mysql backup service";
        enable = true;
        serviceConfig = {
          User = cfg.user;
          PermissionsStartOnly = true;
        };
        preStart = ''
          mkdir -m 0700 -p ${cfg.location}
          chown -R ${cfg.user} ${cfg.location}
        '';
        script = backupScript;
      };
    };
  };

}