nixos: Add borgthin module
This commit is contained in:
@@ -81,6 +81,10 @@
|
||||
fsType = "ext4";
|
||||
neededForBoot = true;
|
||||
};
|
||||
"/mnt/backup" = {
|
||||
device = "/dev/main/tmp-backup";
|
||||
fsType = "ext4";
|
||||
};
|
||||
};
|
||||
|
||||
services = {
|
||||
@@ -240,6 +244,9 @@
|
||||
#deploy.generate.system.mode = "boot";
|
||||
secrets = {
|
||||
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPIijqzAWF6OxKr4aeCa1TAc5xGn4rdIjVTt0wAPU6uY";
|
||||
files = {
|
||||
"colony/borg-pass.txt" = {};
|
||||
};
|
||||
};
|
||||
|
||||
server.enable = true;
|
||||
@@ -255,6 +262,34 @@
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
borgthin = {
|
||||
enable = true;
|
||||
jobs = {
|
||||
main = {
|
||||
repo = "/mnt/backup/main";
|
||||
passFile = config.age.secrets."colony/borg-pass.txt".path;
|
||||
lvs = map (lv: "main/${lv}") [
|
||||
"colony-persist"
|
||||
"vm-shill-persist"
|
||||
"minio"
|
||||
"oci"
|
||||
"vm-estuary-persist"
|
||||
"vm-whale2-persist"
|
||||
];
|
||||
compression = "zstd,5";
|
||||
extraCreateArgs = [ "--stats" ];
|
||||
prune.keep = {
|
||||
last = 1;
|
||||
within = "1d";
|
||||
daily = 7;
|
||||
weekly = 4;
|
||||
monthly = 12;
|
||||
yearly = -1;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@@ -16,5 +16,6 @@
|
||||
nginx-sso = ./nginx-sso.nix;
|
||||
gui = ./gui.nix;
|
||||
l2mesh = ./l2mesh.nix;
|
||||
borgthin = ./borgthin.nix;
|
||||
};
|
||||
}
|
||||
|
150
nixos/modules/borgthin.nix
Normal file
150
nixos/modules/borgthin.nix
Normal file
@@ -0,0 +1,150 @@
|
||||
{ lib, pkgs, config, ... }:
|
||||
let
|
||||
inherit (builtins) substring match;
|
||||
inherit (lib)
|
||||
nameValuePair optional optionalString optionalAttrs mapAttrs' mapAttrsToList concatStringsSep
|
||||
concatMapStringsSep mkIf;
|
||||
inherit (lib.my) mkOpt' mkBoolOpt';
|
||||
|
||||
jobType = with lib.types; submodule ({ name, ... }@args:
|
||||
let
|
||||
cfg = args.config;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
repo = mkOpt' str null "borg repository URL";
|
||||
passFile = mkOpt' (nullOr str) null "Path to file containing passphrase";
|
||||
|
||||
archivePrefix = mkOpt' str "${config.networking.hostName}-${name}-" "Prefix to start new archives with";
|
||||
dateFormat = mkOpt' str "+%Y-%m-%dT%H:%M:%S" "Format passed to the date command";
|
||||
compression = mkOpt' str "zstd,3" "Compression options";
|
||||
lvs = mkOpt' (listOf str) null "Thin LVs to backup (vg/lv format)";
|
||||
prune = {
|
||||
pattern = mkOpt' str "sh:${cfg.archivePrefix}*" "Borg pattern to select archives for pruning";
|
||||
keep = mkOpt' (attrsOf (either int str)) { } "Borg pruning params";
|
||||
};
|
||||
|
||||
extraArgs = mkOpt' (listOf str) [ "--iec" ] "Extra args to pass to all borg commands";
|
||||
extraCreateArgs = mkOpt' (listOf str) [ ] "Extra args to pass to tcreate command";
|
||||
environment = mkOpt' (attrsOf str) { } "Extra environment variables to pass to borg";
|
||||
|
||||
timer = {
|
||||
at = mkOpt' (either str (listOf str)) "5:00" "systemd calendar time(s) to run backup at";
|
||||
persistent = mkBoolOpt' false "Persistent systemd timer";
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
cfg' = config.my.borgthin;
|
||||
|
||||
isLocalPath = x:
|
||||
substring 0 1 x == "/" # absolute path
|
||||
|| substring 0 1 x == "." # relative path
|
||||
|| match "[.*:.*]" == null; # not machine:path
|
||||
|
||||
argStr = concatMapStringsSep " " (a: ''"${a}"'');
|
||||
|
||||
mkEnv = name: cfg: (rec {
|
||||
BORG_BASE_DIR = "/var/lib/borgthin";
|
||||
BORG_CONFIG_DIR = "${BORG_BASE_DIR}/config";
|
||||
BORG_CACHE_DIR = "/var/cache/borgthin";
|
||||
|
||||
BORG_REPO = cfg.repo;
|
||||
}) //
|
||||
(optionalAttrs (cfg.passFile != null) {
|
||||
BORG_PASSCOMMAND = "cat ${cfg.passFile}";
|
||||
}) //
|
||||
cfg.environment;
|
||||
|
||||
# utility function around makeWrapper
|
||||
mkWrapperDrv = {
|
||||
original, name,
|
||||
set ? { }, addFlags ? [ ],
|
||||
}:
|
||||
pkgs.runCommand "${name}-wrapper" {
|
||||
nativeBuildInputs = [ pkgs.makeWrapper ];
|
||||
} ''
|
||||
makeWrapper "${original}" "$out/bin/${name}" \
|
||||
${concatStringsSep " \\\n " (
|
||||
(mapAttrsToList (name: value: ''--set ${name} "${value}"'') set) ++
|
||||
(map (f: ''--add-flags "${f}"'') addFlags)
|
||||
)}
|
||||
'';
|
||||
mkBorgWrapper = name: cfg: mkWrapperDrv {
|
||||
original = "${cfg'.package}/bin/borgthin";
|
||||
name = "borgthin-job-${name}";
|
||||
set = mkEnv name cfg;
|
||||
addFlags = cfg.extraArgs;
|
||||
};
|
||||
|
||||
mkKeepArgs = keep:
|
||||
# If cfg.prune.keep e.g. has a yearly attribute,
|
||||
# its content is passed on as --keep-yearly
|
||||
concatStringsSep " "
|
||||
(mapAttrsToList (x: y: "--keep-${x}=${toString y}") keep);
|
||||
|
||||
mkService = name: cfg: nameValuePair "borgthin-job-${name}" {
|
||||
description = "borgthin backup job ${name}";
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
StateDirectory = "borgthin";
|
||||
CacheDirectory = "borgthin";
|
||||
|
||||
# Only run when no other process is using CPU or disk
|
||||
CPUSchedulingPolicy = "idle";
|
||||
IOSchedulingClass = "idle";
|
||||
};
|
||||
environment = mkEnv name cfg;
|
||||
|
||||
path = [ cfg'.lvmPackage cfg'.thinToolsPackage cfg'.package ];
|
||||
script = ''
|
||||
extraArgs="${argStr cfg.extraArgs}"
|
||||
borgthin $extraArgs tcreate \
|
||||
--compression "${cfg.compression}" \
|
||||
${argStr cfg.extraCreateArgs} \
|
||||
"${cfg.archivePrefix}$(date "${cfg.dateFormat}")" \
|
||||
${concatStringsSep " " cfg.lvs}
|
||||
'' + optionalString (cfg.prune.keep != { }) ''
|
||||
borgthin $extraArgs prune \
|
||||
--match-archives "${cfg.prune.pattern}" \
|
||||
${mkKeepArgs cfg.prune.keep}
|
||||
borgthin $extraArgs compact
|
||||
'';
|
||||
};
|
||||
|
||||
mkTimer = name: cfg: nameValuePair "borgthin-job-${name}" {
|
||||
description = "borgthin backup job ${name} timer";
|
||||
after = optional (cfg.timer.persistent && !isLocalPath cfg.repo) "network-online.target";
|
||||
timerConfig = {
|
||||
Persistent = cfg.timer.persistent;
|
||||
OnCalendar = cfg.timer.at;
|
||||
};
|
||||
wantedBy = [ "timers.target" ];
|
||||
};
|
||||
in
|
||||
{
|
||||
options.my.borgthin = with lib.types; {
|
||||
enable = mkBoolOpt' false "Whether to enable borgthin jobs";
|
||||
lvmPackage = mkOpt' package pkgs.lvm2 "Packge containing LVM tools";
|
||||
thinToolsPackage = mkOpt' package pkgs.thin-provisioning-tools "Package containing thin-provisioning-tools";
|
||||
package = mkOpt' package pkgs.borgthin "borgthin package";
|
||||
jobs = mkOpt' (attrsOf jobType) { } "borgthin jobs";
|
||||
};
|
||||
|
||||
config = mkIf cfg'.enable {
|
||||
environment.systemPackages =
|
||||
[ cfg'.package ] ++
|
||||
(mapAttrsToList mkBorgWrapper cfg'.jobs);
|
||||
|
||||
systemd = {
|
||||
services = mapAttrs' mkService cfg'.jobs;
|
||||
timers = mapAttrs' mkTimer cfg'.jobs;
|
||||
};
|
||||
|
||||
my.tmproot.persistence.config.directories = [
|
||||
"/var/lib/borgthin"
|
||||
"/var/cache/borgthin"
|
||||
];
|
||||
};
|
||||
}
|
||||
|
@@ -73,6 +73,7 @@ in
|
||||
overlays = [
|
||||
inputs.deploy-rs.overlay
|
||||
inputs.sharry.overlays.default
|
||||
inputs.borgthin.overlays.default
|
||||
];
|
||||
config = {
|
||||
allowUnfree = true;
|
||||
|
Reference in New Issue
Block a user