118 lines
3.4 KiB
Nix
118 lines
3.4 KiB
Nix
# SPDX-FileCopyrightText: 2020-2024 Simon Bruder <simon@sbruder.de>
|
||
#
|
||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||
|
||
{ pkgs, config, lib, ... }:
|
||
let
|
||
cfg = config.sbruder.restic.backups.system;
|
||
|
||
excludes = [
|
||
# Caches
|
||
"/home/*/Downloads/"
|
||
"/home/*/.cache/"
|
||
"/home/*/**/cache/"
|
||
"/home/*/.local/share/Trash" # some gui applications use it
|
||
"/root/.cache"
|
||
"/data/cache/"
|
||
"/var/cache/"
|
||
|
||
# Rust
|
||
"/home/*/.rustup/toolchains/"
|
||
"/home/*/.cargo"
|
||
|
||
# Misc
|
||
"/home/*/mount"
|
||
"/home/*/mounts"
|
||
|
||
# Docker (state should be kept somewhere else)
|
||
"/home/*/.local/share/containers" # podman
|
||
"/var/lib/containers/"
|
||
"/var/lib/docker/"
|
||
|
||
# Static configuration (generated from this repository)
|
||
"/etc/static/"
|
||
] ++ cfg.extraExcludes;
|
||
excludesFile = pkgs.writeText "excludes.txt" (lib.concatStringsSep "\n" excludes);
|
||
|
||
# HACK: NixOS’ nftables implementation runs nft -c inside the build sandbox,
|
||
# where the target host’s cgroups are not available,
|
||
# and therefore fails.
|
||
# This is there to allow my home router to put backup traffic into the right qdisc,
|
||
# as the ip address and port are also used for other things.
|
||
# This is somewhat of an abuse of the DSCP mark.
|
||
qosRules = pkgs.writeText "restic-qos.nft" ''
|
||
table inet restic
|
||
delete table inet restic
|
||
|
||
table inet restic {
|
||
chain output {
|
||
type filter hook output priority mangle
|
||
ip version 4 socket cgroupv2 level 1 "restic.slice" ip dscp set af12 return
|
||
ip6 version 6 socket cgroupv2 level 1 "restic.slice" ip6 dscp set af12 return
|
||
}
|
||
}
|
||
'';
|
||
in
|
||
{
|
||
options.sbruder.restic.backups.system = {
|
||
enable = lib.mkEnableOption "restic system backup";
|
||
timerConfig = lib.mkOption {
|
||
type = with lib.types; attrsOf str;
|
||
default = {
|
||
OnCalendar = "18:00";
|
||
RandomizedDelaySec = "2h";
|
||
};
|
||
};
|
||
extraPaths = lib.mkOption {
|
||
type = lib.types.listOf lib.types.str;
|
||
default = [ ];
|
||
example = [ "/data" ];
|
||
};
|
||
extraExcludes = lib.mkOption {
|
||
type = lib.types.listOf lib.types.str;
|
||
default = [ ];
|
||
};
|
||
uploadLimit = lib.mkOption {
|
||
type = lib.types.nullOr lib.types.int;
|
||
default = null;
|
||
};
|
||
qos = (lib.mkEnableOption "QoS marking (DSCP AF12) of outgoing packets") // { default = !(isNull cfg.uploadLimit); };
|
||
};
|
||
|
||
config = lib.mkIf cfg.enable {
|
||
services.restic.backups.system = {
|
||
inherit (cfg) timerConfig;
|
||
repositoryFile = config.sops.secrets.restic-repository.path;
|
||
passwordFile = config.sops.secrets.restic-password.path;
|
||
paths = [
|
||
"/etc"
|
||
"/home"
|
||
"/root"
|
||
"/srv"
|
||
"/var"
|
||
] ++ cfg.extraPaths;
|
||
extraBackupArgs = [
|
||
"--compression auto"
|
||
"--exclude-caches"
|
||
"--exclude-file=${excludesFile}"
|
||
"--tag system"
|
||
"--verbose"
|
||
] ++ lib.optional (cfg.uploadLimit != null) "--limit-upload=${toString cfg.uploadLimit}";
|
||
} // (lib.optionalAttrs cfg.qos {
|
||
backupPrepareCommand = ''
|
||
${pkgs.nftables}/bin/nft -f ${qosRules}
|
||
'';
|
||
backupCleanupCommand = ''
|
||
${pkgs.nftables}/bin/nft delete table inet restic
|
||
'';
|
||
});
|
||
|
||
systemd.services."restic-backups-system".serviceConfig = {
|
||
"Nice" = 10;
|
||
"IOSchedulingClass" = "best-effort";
|
||
"IOSchedulingPriority" = 7;
|
||
Slice = "restic.slice";
|
||
};
|
||
};
|
||
}
|