155 lines
4.6 KiB
Nix
155 lines
4.6 KiB
Nix
# TODO: https://github.com/lilyinstarlight/foosteros/blob/dfe1ab3eb68bfebfaa709482d52fa04ebdde81c8/config/restic.nix#L23 <- this is better
|
|
{
|
|
config,
|
|
pkgs,
|
|
lib,
|
|
...
|
|
}:
|
|
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}/rootDirectory"
|
|
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 = mkEnableOption "restic";
|
|
paths = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [
|
|
"/home"
|
|
"/var/lib"
|
|
];
|
|
};
|
|
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.
|
|
'';
|
|
};
|
|
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 = 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;
|
|
};
|
|
}
|