parent
ea0ab2fd05
commit
7a878660a7
@ -0,0 +1,141 @@ |
||||
{ config, lib, pkgs, ...}: |
||||
|
||||
with lib; |
||||
|
||||
let |
||||
cfg = config.services.duplicity; |
||||
|
||||
stateDirectory = "/var/lib/duplicity"; |
||||
|
||||
localTarget = if hasPrefix "file://" cfg.targetUrl |
||||
then removePrefix "file://" cfg.targetUrl else null; |
||||
|
||||
in { |
||||
options.services.duplicity = { |
||||
enable = mkEnableOption "backups with duplicity"; |
||||
|
||||
root = mkOption { |
||||
type = types.path; |
||||
default = "/"; |
||||
description = '' |
||||
Root directory to backup. |
||||
''; |
||||
}; |
||||
|
||||
include = mkOption { |
||||
type = types.listOf types.str; |
||||
default = []; |
||||
example = [ "/home" ]; |
||||
description = '' |
||||
List of paths to include into the backups. See the FILE SELECTION |
||||
section in <citerefentry><refentrytitle>duplicity</refentrytitle> |
||||
<manvolnum>1</manvolnum></citerefentry> for details on the syntax. |
||||
''; |
||||
}; |
||||
|
||||
exclude = mkOption { |
||||
type = types.listOf types.str; |
||||
default = []; |
||||
description = '' |
||||
List of paths to exclude from backups. See the FILE SELECTION section in |
||||
<citerefentry><refentrytitle>duplicity</refentrytitle> |
||||
<manvolnum>1</manvolnum></citerefentry> for details on the syntax. |
||||
''; |
||||
}; |
||||
|
||||
targetUrl = mkOption { |
||||
type = types.str; |
||||
example = "s3://host:port/prefix"; |
||||
description = '' |
||||
Target url to backup to. See the URL FORMAT section in |
||||
<citerefentry><refentrytitle>duplicity</refentrytitle> |
||||
<manvolnum>1</manvolnum></citerefentry> for supported urls. |
||||
''; |
||||
}; |
||||
|
||||
secretFile = mkOption { |
||||
type = types.nullOr types.path; |
||||
default = null; |
||||
description = '' |
||||
Path of a file containing secrets (gpg passphrase, access key...) in |
||||
the format of EnvironmentFile as described by |
||||
<citerefentry><refentrytitle>systemd.exec</refentrytitle> |
||||
<manvolnum>5</manvolnum></citerefentry>. For example: |
||||
<programlisting> |
||||
PASSPHRASE=<replaceable>...</replaceable> |
||||
AWS_ACCESS_KEY_ID=<replaceable>...</replaceable> |
||||
AWS_SECRET_ACCESS_KEY=<replaceable>...</replaceable> |
||||
</programlisting> |
||||
''; |
||||
}; |
||||
|
||||
frequency = mkOption { |
||||
type = types.nullOr types.str; |
||||
default = "daily"; |
||||
description = '' |
||||
Run duplicity with the given frequency (see |
||||
<citerefentry><refentrytitle>systemd.time</refentrytitle> |
||||
<manvolnum>7</manvolnum></citerefentry> for the format). |
||||
If null, do not run automatically. |
||||
''; |
||||
}; |
||||
|
||||
extraFlags = mkOption { |
||||
type = types.listOf types.str; |
||||
default = []; |
||||
example = [ "--full-if-older-than" "1M" ]; |
||||
description = '' |
||||
Extra command-line flags passed to duplicity. See |
||||
<citerefentry><refentrytitle>duplicity</refentrytitle> |
||||
<manvolnum>1</manvolnum></citerefentry>. |
||||
''; |
||||
}; |
||||
}; |
||||
|
||||
config = mkIf cfg.enable { |
||||
systemd = { |
||||
services.duplicity = { |
||||
description = "backup files with duplicity"; |
||||
|
||||
environment.HOME = stateDirectory; |
||||
|
||||
serviceConfig = { |
||||
ExecStart = '' |
||||
${pkgs.duplicity}/bin/duplicity ${escapeShellArgs ( |
||||
[ |
||||
cfg.root |
||||
cfg.targetUrl |
||||
"--archive-dir" stateDirectory |
||||
] |
||||
++ concatMap (p: [ "--include" p ]) cfg.include |
||||
++ concatMap (p: [ "--exclude" p ]) cfg.exclude |
||||
++ cfg.extraFlags)} |
||||
''; |
||||
PrivateTmp = true; |
||||
ProtectSystem = "strict"; |
||||
ProtectHome = "read-only"; |
||||
StateDirectory = baseNameOf stateDirectory; |
||||
} // optionalAttrs (localTarget != null) { |
||||
ReadWritePaths = localTarget; |
||||
} // optionalAttrs (cfg.secretFile != null) { |
||||
EnvironmentFile = cfg.secretFile; |
||||
}; |
||||
} // optionalAttrs (cfg.frequency != null) { |
||||
startAt = cfg.frequency; |
||||
}; |
||||
|
||||
tmpfiles.rules = optional (localTarget != null) "d ${localTarget} 0700 root root -"; |
||||
}; |
||||
|
||||
assertions = singleton { |
||||
# Duplicity will fail if the last file selection option is an include. It |
||||
# is not always possible to detect but this simple case can be caught. |
||||
assertion = cfg.include != [] -> cfg.exclude != [] || cfg.extraFlags != []; |
||||
message = '' |
||||
Duplicity will fail if you only specify included paths ("Because the |
||||
default is to include all files, the expression is redundant. Exiting |
||||
because this probably isn't what you meant.") |
||||
''; |
||||
}; |
||||
}; |
||||
} |
Loading…
Reference in new issue