modules/restic: snapshot all btrfs subvolumes
This commit is contained in:
parent
2327a171b8
commit
ebf69d94dd
7 changed files with 196 additions and 73 deletions
|
@ -4,6 +4,7 @@
|
|||
./common-settings/autoupgrade.nix
|
||||
./common-settings/nix-conf.nix
|
||||
./common-settings/proxy-server.nix
|
||||
./disk-partitions
|
||||
./restic.nix
|
||||
./vaultwarden.nix
|
||||
./prometheus
|
||||
|
|
|
@ -6,63 +6,150 @@
|
|||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib)
|
||||
mkEnableOption
|
||||
mkOption
|
||||
mkDefault
|
||||
mkIf
|
||||
types
|
||||
getExe
|
||||
;
|
||||
cfg = config.custom.restic;
|
||||
mapBtrfsRoots =
|
||||
rootDir:
|
||||
let
|
||||
backupDir = lib.removeSuffix "/" "/backup${rootDir}";
|
||||
slash = if rootDir == "/" then "" else "/";
|
||||
awk = getExe pkgs.gawk;
|
||||
continueIfInExclude = ''
|
||||
exclude_subv="${toString cfg.btrfsExcludeSubvolume}"
|
||||
found=false
|
||||
for subv in $exclude_subv; do
|
||||
if [[ "$subvol" == "$subv" ]]; then
|
||||
found=true
|
||||
echo "$subvol is in exclude subvolumes, skipped"
|
||||
break
|
||||
fi
|
||||
done
|
||||
$found && continue
|
||||
'';
|
||||
in
|
||||
{
|
||||
backupPrepareCommand = ''
|
||||
echo "Creating snapshot for ${rootDir}"
|
||||
subvolumes=$(${pkgs.btrfs-progs}/bin/btrfs subvolume list -o "${rootDir}" | ${awk} '{print $NF}')
|
||||
mkdir -p "${backupDir}"
|
||||
${pkgs.btrfs-progs}/bin/btrfs subvolume snapshot -r "${rootDir}" "${backupDir}/rootfs"
|
||||
for subvol in $subvolumes; do
|
||||
${continueIfInExclude}
|
||||
[[ /"$subvol" == "${backupDir}"* ]] && continue
|
||||
|
||||
snapshot_path=$(dirname "${backupDir}/$subvol")
|
||||
mkdir -p "$snapshot_path"
|
||||
|
||||
echo "Creating snapshot for subvolume: $subvol at $snapshot_path"
|
||||
${pkgs.btrfs-progs}/bin/btrfs subvolume snapshot -r "${rootDir}${slash}$subvol" "$snapshot_path"
|
||||
done
|
||||
'';
|
||||
|
||||
# Note that all the manually created snapshots under backupDir will also be cleaned
|
||||
backupCleanupCommand = ''
|
||||
# Only find snapshots under backup directory
|
||||
subvolumes=$(${pkgs.btrfs-progs}/bin/btrfs subvolume list -s -o "${backupDir}" | ${awk} '{print $NF}')
|
||||
for subvol in $subvolumes; do
|
||||
echo "Removing snapshot for subvolume: $subvol"
|
||||
${pkgs.btrfs-progs}/bin/btrfs subvolume delete "$subvol"
|
||||
done
|
||||
'';
|
||||
};
|
||||
|
||||
btrfsFs = lib.attrsets.filterAttrs (
|
||||
n: v: v.fsType == "btrfs" && ((isNull cfg.btrfsRoots) || (builtins.elem n cfg.btrfsRoots))
|
||||
) config.fileSystems;
|
||||
btrfsFsRoot = builtins.attrNames btrfsFs;
|
||||
btrfsCommands = (map mapBtrfsRoots btrfsFsRoot);
|
||||
in
|
||||
{
|
||||
options = {
|
||||
custom.restic = {
|
||||
enable = lib.mkEnableOption "restic";
|
||||
paths = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
enable = mkEnableOption "restic";
|
||||
paths = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [
|
||||
"/home"
|
||||
"/var/lib"
|
||||
];
|
||||
};
|
||||
prune = lib.mkEnableOption "auto prune remote restic repo";
|
||||
repositoryFile = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "";
|
||||
prune = mkEnableOption "auto prune remote restic repo";
|
||||
btrfsRoots = mkOption {
|
||||
type = types.nullOr (types.listOf types.str);
|
||||
default = [ "/" ];
|
||||
description = ''
|
||||
Includeded btrfs roots. `null` means snapshot all btrfs filesystems under config.fileSystems.
|
||||
'';
|
||||
};
|
||||
passwordFile = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "";
|
||||
btrfsExcludeSubvolume = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [
|
||||
"nix"
|
||||
"rootfs"
|
||||
"swap"
|
||||
"var/tmp"
|
||||
];
|
||||
example = lib.literalExpression ''
|
||||
[ "var/tmp" "srv" ]
|
||||
'';
|
||||
};
|
||||
backupPrepareCommand = mkOption {
|
||||
type = types.listOf types.str;
|
||||
};
|
||||
backupCleanupCommand = mkOption {
|
||||
type = types.listOf types.str;
|
||||
};
|
||||
};
|
||||
};
|
||||
config = lib.mkIf cfg.enable {
|
||||
services.restic.backups.${config.networking.hostName} = lib.mkMerge [
|
||||
{
|
||||
repositoryFile = cfg.repositoryFile;
|
||||
passwordFile = cfg.passwordFile;
|
||||
exclude = [
|
||||
"/home/*/.cache"
|
||||
"/home/*/.cargo"
|
||||
"/home/*/.local/share/Steam"
|
||||
"/home/*/.local/share/flatpak"
|
||||
];
|
||||
timerConfig = {
|
||||
OnCalendar = "00:05";
|
||||
RandomizedDelaySec = "5h";
|
||||
};
|
||||
pruneOpts = lib.mkIf cfg.prune [
|
||||
"--keep-daily 7"
|
||||
"--keep-weekly 5"
|
||||
"--keep-monthly 12"
|
||||
"--keep-yearly 75"
|
||||
];
|
||||
paths = lib.mkDefault cfg.paths;
|
||||
initialize = true;
|
||||
}
|
||||
(lib.mkIf (config.fileSystems."/".fsType == "btrfs") {
|
||||
backupPrepareCommand = ''
|
||||
${pkgs.btrfs-progs}/bin/btrfs subvolume snapshot -r / backup
|
||||
'';
|
||||
backupCleanupCommand = ''
|
||||
${pkgs.btrfs-progs}/bin/btrfs subvolume delete /backup
|
||||
'';
|
||||
paths = map (p: "/backup" + p) cfg.paths;
|
||||
})
|
||||
];
|
||||
config = mkIf cfg.enable {
|
||||
services.restic.backups.${config.networking.hostName} = {
|
||||
repositoryFile = config.sops.secrets."restic/repo_url".path;
|
||||
passwordFile = config.sops.secrets."restic/repo_password".path;
|
||||
exclude = [
|
||||
"**/.cache"
|
||||
"**/.local/share/Steam"
|
||||
"**/.local/share/flatpak"
|
||||
|
||||
"**/.cargo"
|
||||
"**/.rustup"
|
||||
|
||||
"**/node_modules"
|
||||
|
||||
"*.pyc"
|
||||
"*.pyo"
|
||||
"**/__pycache__"
|
||||
"**/.virtualenvs"
|
||||
"**/.venv"
|
||||
|
||||
# temp files / lock files
|
||||
"*.sqlite-wal"
|
||||
"*.sqlite-shm"
|
||||
"*.db-wal"
|
||||
"*.db-shm"
|
||||
];
|
||||
timerConfig = {
|
||||
OnCalendar = "00:05";
|
||||
RandomizedDelaySec = "5h";
|
||||
};
|
||||
pruneOpts = mkIf cfg.prune [
|
||||
"--keep-daily 7"
|
||||
"--keep-weekly 5"
|
||||
"--keep-monthly 12"
|
||||
"--keep-yearly 75"
|
||||
];
|
||||
paths = mkDefault cfg.paths;
|
||||
initialize = true;
|
||||
backupPrepareCommand = lib.strings.concatLines cfg.backupPrepareCommand;
|
||||
backupCleanupCommand = lib.strings.concatLines cfg.backupCleanupCommand;
|
||||
};
|
||||
custom.restic.backupPrepareCommand = map (x: x.backupPrepareCommand) btrfsCommands;
|
||||
custom.restic.backupCleanupCommand = map (x: x.backupCleanupCommand) btrfsCommands;
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue