{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.syncoid;
in {
# Interface
options.services.syncoid = {
enable = mkEnableOption "Syncoid ZFS synchronization service";
interval = mkOption {
type = types.str;
default = "hourly";
example = "*-*-* *:15:00";
description = ''
Run syncoid at this interval. The default is to run hourly.
The format is described in
systemd.time7.
'';
};
user = mkOption {
type = types.str;
default = "root";
example = "backup";
description = ''
The user for the service. Sudo or ZFS privilege delegation must be
configured to use a user other than root.
'';
};
sshKey = mkOption {
type = types.nullOr types.path;
# Prevent key from being copied to store
apply = mapNullable toString;
default = null;
description = ''
SSH private key file to use to login to the remote system. Can be
overridden in individual commands.
'';
};
commonArgs = mkOption {
type = types.listOf types.str;
default = [];
example = [ "--no-sync-snap" ];
description = ''
Arguments to add to every syncoid command, unless disabled for that
command. See
for available options.
'';
};
commands = mkOption {
type = types.attrsOf (types.submodule ({ name, ... }: {
options = {
source = mkOption {
type = types.str;
example = "pool/dataset";
description = ''
Source ZFS dataset. Can be either local or remote. Defaults to
the attribute name.
'';
};
target = mkOption {
type = types.str;
example = "user@server:pool/dataset";
description = ''
Target ZFS dataset. Can be either local
(pool/dataset) or remote
(user@server:pool/dataset).
'';
};
recursive = mkOption {
type = types.bool;
default = false;
description = ''
Whether to also transfer child datasets.
'';
};
sshKey = mkOption {
type = types.nullOr types.path;
# Prevent key from being copied to store
apply = mapNullable toString;
description = ''
SSH private key file to use to login to the remote system.
Defaults to option.
'';
};
sendOptions = mkOption {
type = types.separatedString " ";
default = "";
example = "Lc e";
description = ''
Advanced options to pass to zfs send. Options are specified
without their leading dashes and separated by spaces.
'';
};
recvOptions = mkOption {
type = types.separatedString " ";
default = "";
example = "ux recordsize o compression=lz4";
description = ''
Advanced options to pass to zfs recv. Options are specified
without their leading dashes and separated by spaces.
'';
};
useCommonArgs = mkOption {
type = types.bool;
default = true;
description = ''
Whether to add the configured common arguments to this command.
'';
};
extraArgs = mkOption {
type = types.listOf types.str;
default = [];
example = [ "--sshport 2222" ];
description = "Extra syncoid arguments for this command.";
};
};
config = {
source = mkDefault name;
sshKey = mkDefault cfg.sshKey;
};
}));
default = {};
example."pool/test".target = "root@target:pool/test";
description = "Syncoid commands to run.";
};
};
# Implementation
config = mkIf cfg.enable {
systemd.services.syncoid = {
description = "Syncoid ZFS synchronization service";
script = concatMapStringsSep "\n" (c: lib.escapeShellArgs
([ "${pkgs.sanoid}/bin/syncoid" ]
++ (optionals c.useCommonArgs cfg.commonArgs)
++ (optional c.recursive "-r")
++ (optionals (c.sshKey != null) [ "--sshkey" c.sshKey ])
++ c.extraArgs
++ [ "--sendoptions" c.sendOptions
"--recvoptions" c.recvOptions
c.source c.target
])) (attrValues cfg.commands);
after = [ "zfs.target" ];
serviceConfig.User = cfg.user;
startAt = cfg.interval;
};
};
meta.maintainers = with maintainers; [ lopsided98 ];
}