1{ config, lib, pkgs, ... }:
2
3with lib;
4{
5 options.services.restic.backups = mkOption {
6 description = ''
7 Periodic backups to create with Restic.
8 '';
9 type = types.attrsOf (types.submodule ({ name, ... }: {
10 options = {
11 passwordFile = mkOption {
12 type = types.str;
13 description = ''
14 Read the repository password from a file.
15 '';
16 example = "/etc/nixos/restic-password";
17 };
18
19 s3CredentialsFile = mkOption {
20 type = with types; nullOr str;
21 default = null;
22 description = ''
23 file containing the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
24 for an S3-hosted repository, in the format of an EnvironmentFile
25 as described by systemd.exec(5)
26 '';
27 };
28
29 repository = mkOption {
30 type = types.str;
31 description = ''
32 repository to backup to.
33 '';
34 example = "sftp:backup@192.168.1.100:/backups/${name}";
35 };
36
37 paths = mkOption {
38 type = types.listOf types.str;
39 default = [];
40 description = ''
41 Which paths to backup.
42 '';
43 example = [
44 "/var/lib/postgresql"
45 "/home/user/backup"
46 ];
47 };
48
49 timerConfig = mkOption {
50 type = types.attrsOf types.str;
51 default = {
52 OnCalendar = "daily";
53 };
54 description = ''
55 When to run the backup. See man systemd.timer for details.
56 '';
57 example = {
58 OnCalendar = "00:05";
59 RandomizedDelaySec = "5h";
60 };
61 };
62
63 user = mkOption {
64 type = types.str;
65 default = "root";
66 description = ''
67 As which user the backup should run.
68 '';
69 example = "postgresql";
70 };
71
72 extraBackupArgs = mkOption {
73 type = types.listOf types.str;
74 default = [];
75 description = ''
76 Extra arguments passed to restic backup.
77 '';
78 example = [
79 "--exclude-file=/etc/nixos/restic-ignore"
80 ];
81 };
82
83 extraOptions = mkOption {
84 type = types.listOf types.str;
85 default = [];
86 description = ''
87 Extra extended options to be passed to the restic --option flag.
88 '';
89 example = [
90 "sftp.command='ssh backup@192.168.1.100 -i /home/user/.ssh/id_rsa -s sftp'"
91 ];
92 };
93
94 initialize = mkOption {
95 type = types.bool;
96 default = false;
97 description = ''
98 Create the repository if it doesn't exist.
99 '';
100 };
101 };
102 }));
103 default = {};
104 example = {
105 localbackup = {
106 paths = [ "/home" ];
107 repository = "/mnt/backup-hdd";
108 passwordFile = "/etc/nixos/secrets/restic-password";
109 initialize = true;
110 };
111 remotebackup = {
112 paths = [ "/home" ];
113 repository = "sftp:backup@host:/backups/home";
114 passwordFile = "/etc/nixos/secrets/restic-password";
115 extraOptions = [
116 "sftp.command='ssh backup@host -i /etc/nixos/secrets/backup-private-key -s sftp'"
117 ];
118 timerConfig = {
119 OnCalendar = "00:05";
120 RandomizedDelaySec = "5h";
121 };
122 };
123 };
124 };
125
126 config = {
127 systemd.services =
128 mapAttrs' (name: backup:
129 let
130 extraOptions = concatMapStrings (arg: " -o ${arg}") backup.extraOptions;
131 resticCmd = "${pkgs.restic}/bin/restic${extraOptions}";
132 in nameValuePair "restic-backups-${name}" ({
133 environment = {
134 RESTIC_PASSWORD_FILE = backup.passwordFile;
135 RESTIC_REPOSITORY = backup.repository;
136 };
137 path = with pkgs; [
138 openssh
139 ];
140 restartIfChanged = false;
141 serviceConfig = {
142 Type = "oneshot";
143 ExecStart = "${resticCmd} backup ${concatStringsSep " " backup.extraBackupArgs} ${concatStringsSep " " backup.paths}";
144 User = backup.user;
145 } // optionalAttrs (backup.s3CredentialsFile != null) {
146 EnvironmentFile = backup.s3CredentialsFile;
147 };
148 } // optionalAttrs backup.initialize {
149 preStart = ''
150 ${resticCmd} snapshots || ${resticCmd} init
151 '';
152 })
153 ) config.services.restic.backups;
154 systemd.timers =
155 mapAttrs' (name: backup: nameValuePair "restic-backups-${name}" {
156 wantedBy = [ "timers.target" ];
157 timerConfig = backup.timerConfig;
158 }) config.services.restic.backups;
159 };
160}