nixos-config/modules/restic/system.nix

118 lines
3.4 KiB
Nix
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 hosts 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";
};
};
}