restic/vm-image: Init

This commit is contained in:
Simon Bruder 2024-08-22 22:36:47 +02:00
parent 0fa4e6d855
commit 5693e6b75d
Signed by: simon
GPG key ID: 347FF8699CDA0776
3 changed files with 114 additions and 6 deletions

View file

@ -24,6 +24,16 @@ let
}; };
in in
{ {
sbruder.restic = {
enable = true;
backups.vm-image = {
enable = true;
lvm.lvs = [
"hiroshi"
];
};
};
virtualisation.libvirtd = { virtualisation.libvirtd = {
enable = true; enable = true;
qemu.package = pkgs.qemu_kvm; qemu.package = pkgs.qemu_kvm;

View file

@ -10,7 +10,7 @@ let
sftpPort = 23; sftpPort = 23;
repository = "sftp://${sftpTarget}:${toString sftpPort}/personal"; repository = "sftp://${sftpTarget}:${toString sftpPort}/personal";
mkPruneConfig = { tag, timerConfig }: { mkPruneConfig = { tag, timerConfig, opts }: {
inherit repository timerConfig; inherit repository timerConfig;
passwordFile = config.sops.secrets.restic-password.path; passwordFile = config.sops.secrets.restic-password.path;
paths = [ ]; paths = [ ];
@ -20,18 +20,15 @@ let
]; ];
pruneOpts = [ pruneOpts = [
"--compression auto" "--compression auto"
"--keep-daily 7"
"--keep-monthly 12"
"--keep-weekly 5"
"--keep-yearly 10"
"--tag ${tag}" "--tag ${tag}"
"--verbose" "--verbose"
]; ] ++ opts;
}; };
in in
{ {
imports = [ imports = [
./system.nix ./system.nix
./vm-image.nix
]; ];
options.sbruder.restic = { options.sbruder.restic = {
@ -71,6 +68,23 @@ in
OnCalendar = "*-1/2-07 03:00:00"; OnCalendar = "*-1/2-07 03:00:00";
RandomizedDelaySec = "4h"; RandomizedDelaySec = "4h";
}; };
opts = [
"--keep-daily 7"
"--keep-monthly 12"
"--keep-weekly 5"
"--keep-yearly 10"
];
};
vm-image-prune = mkPruneConfig {
tag = "vm-image";
timerConfig = {
OnCalendar = "06:00";
RandomizedDelaySec = "1h";
};
opts = [
"--keep-last 1"
];
}; };
}; };
}) })

View file

@ -0,0 +1,84 @@
# SPDX-FileCopyrightText: 2024 Simon Bruder <simon@sbruder.de>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
{ config, lib, pkgs, ... }:
let
cfg = config.sbruder.restic.backups.vm-image;
in
{
options.sbruder.restic.backups.vm-image = {
enable = lib.mkEnableOption "restic vm image backup";
timerConfig = lib.mkOption {
type = with lib.types; attrsOf str;
default = {
OnCalendar = "03:00";
RandomizedDelaySec = "3h";
};
};
lvm = {
vg = lib.mkOption {
type = lib.types.str;
default = "${config.networking.hostName}-vg";
};
lvs = lib.mkOption {
type = with lib.types; listOf str;
default = [ ];
};
};
};
config = lib.mkIf cfg.enable {
systemd.services = lib.listToAttrs (map
(lv: lib.nameValuePair "restic-backups-vm-image-${lv}" {
wants = [ "network-online.target" ];
after = [ "network-online.target" ];
restartIfChanged = false;
path = with pkgs; [ lvm2 restic ];
script = ''
set -euo pipefail
LV_NAME=${lib.escapeShellArg lv}
FULL_LV_NAME=${lib.escapeShellArg cfg.lvm.vg}/"$LV_NAME"
SNAPSHOT_LV_NAME="restic-snapshot-$LV_NAME"
FULL_SNAPSHOT_LV_NAME=${lib.escapeShellArg cfg.lvm.vg}/"$SNAPSHOT_LV_NAME"
lvcreate --name "$SNAPSHOT_LV_NAME" --snapshot "$FULL_LV_NAME" --permission r --ignoreactivationskip
function cleanup {
lvchange --activate n "$FULL_SNAPSHOT_LV_NAME"
lvremove "$FULL_SNAPSHOT_LV_NAME"
}
trap cleanup EXIT INT TERM
restic backup \
--tag vm-image \
--host ${config.networking.hostName}-hypervisor \
--verbose \
--stdin \
--stdin-filename "$LV_NAME" \
< "/dev/$FULL_SNAPSHOT_LV_NAME"
'';
environment = {
RESTIC_CACHE_DIR = "/var/cache/restic-backups-system"; # hack: reuse system backups directory
RESTIC_REPOSITORY_FILE = config.sops.secrets.restic-repository.path;
RESTIC_PASSWORD_FILE = config.sops.secrets.restic-password.path;
};
serviceConfig = {
Type = "oneshot";
};
})
cfg.lvm.lvs);
systemd.timers = (lib.listToAttrs (map
(lv: lib.nameValuePair "restic-backups-vm-image-${lv}" {
wantedBy = [ "timers.target" ];
inherit (cfg) timerConfig;
})
cfg.lvm.lvs));
};
}