nix-da/options/services/rsync-backup/default.nix

163 lines
3.9 KiB
Nix

{
config,
lib,
pkgs,
options,
...
}:
let
inherit (lib)
mkIf
mkEnableOption
mkOption
concatStringsSep
toList
listToAttrs
;
inherit (lib.types)
listOf
nonEmptyStr
package
str
port
bool
submoduleWith
;
inherit (lib.my) slugify;
cfg = config.modules.services.rsync-backup;
in
{
# Used to back up multiple source destinations to a target destination regularly,
# with possibility to enable incremental backups for versioning.
# TODO: systemd unit
# - [x] script taking configurable args
# - [x] sync from/to paths
# - [ ] support
# - [ ] nfs
# - [ ] sftp
# - [ ] ssh
# - [x] local
# - [x] rsyncd
# - [ ] retention limit for incremental backups
options.modules.services.rsync-backup = {
enable = mkEnableOption "rsync backups";
modules = mkOption {
default = [ ];
type = listOf (submoduleWith {
shorthandOnlyDefinesConfig = true;
modules = toList (import ./module.nix);
specialArgs = {
rootCfg = cfg;
};
});
};
exclude = mkOption {
type = listOf str;
default = [ ".git" ];
};
followGitignore = mkOption {
type = bool;
default = true;
};
package = mkOption {
type = package;
default = pkgs.rsync;
description = ''
The rsync package to use.
'';
};
interval = mkOption {
type = nonEmptyStr;
default = "daily";
};
unitName = mkOption {
type = nonEmptyStr;
default = "rsync-backup";
};
port = mkOption {
type = port;
default = options.modules.server.rsync-daemon.port.default;
};
};
config = mkIf cfg.enable {
systemd.timers = listToAttrs (
map (
mod:
let
name = "${cfg.unitName}-${slugify mod.target.location}";
in
{
inherit name;
value = {
enable = true;
wantedBy = [ "timers.target" ];
unitConfig = {
Description = "Triggers rsync-backups regularly.";
};
timerConfig = {
Unit = "${name}.service";
OnCalendar = mod.interval;
Persistent = true;
};
};
}
) cfg.modules
);
systemd.services = listToAttrs (
map (mod: {
name = "${cfg.unitName}-${slugify mod.target.location}";
value = {
script =
let
destination =
if mod.incremental.enable then "${mod.target.finalLocation}/backup" else mod.target.finalLocation;
sources = concatStringsSep " " (map (s: "\"${s}\"") mod.sources);
in
''
${cfg.package}/bin/rsync \
${mod.flagsFinal} \
${sources} \
"${destination}"
'';
requires = mkIf (mod.target.type == "rsyncd") [ "network.target" ];
unitConfig = {
Description = "Backs up files from a source location to a specified destination.";
};
postStop = ''
if [ "$SERVICE_RESULT" != "success" ]; then
${pkgs.curl}/bin/curl \
-H "Priority: urgent" \
-H "Title: Backup error" \
-d "Backup '${cfg.unitName}-${slugify mod.target.location}' had unexpected behaviour: $SERVICE_RESULT" \
https://ntfy.ccnlc.eu/rsync-backup
fi
'';
serviceConfig = {
Type = "simple";
Restart = "on-failure";
RestartSec = 300;
PrivateDevices = true;
PrivateTmp = true;
ProtectSystem = "full";
ReadOnlyPaths = concatStringsSep " " mod.sources;
IPAddressAllow = "any";
};
};
}) cfg.modules
);
};
}