From dbf84c7f9382d4a1a56bd9f6a8be140fdb2fd95a Mon Sep 17 00:00:00 2001 From: Pim Kunis Date: Wed, 27 Dec 2023 19:14:49 +0100 Subject: [PATCH] create NixOS module to periodically backup data using borgmatic --- machines/default.nix | 1 + modules/custom/backups.nix | 106 +++++++++++++++++++++++++++++++++++++ modules/custom/default.nix | 2 +- 3 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 modules/custom/backups.nix diff --git a/machines/default.nix b/machines/default.nix index ece24b4..8679aa1 100644 --- a/machines/default.nix +++ b/machines/default.nix @@ -38,6 +38,7 @@ nixosModule.custom = { disko.osDiskDevice = "/dev/sda"; + backups.enable = true; dataDisk = { enable = true; diff --git a/modules/custom/backups.nix b/modules/custom/backups.nix new file mode 100644 index 0000000..4a532aa --- /dev/null +++ b/modules/custom/backups.nix @@ -0,0 +1,106 @@ +{ pkgs, lib, config, ... }: +let + cfg = config.custom.backups; + snapshotFile = "/tmp/snapshot.qcow2"; + snapshotMount = "/tmp/snapshot"; + beforeEverything = pkgs.writeShellScriptBin "beforeEverything" '' + ${pkgs.libvirt}/bin/virsh snapshot-create-as --domain ${cfg.domainName} --name backup-${cfg.domainName} --disk-only --quiesce --no-metadata --diskspec vda,snapshot=no --diskspec vdb,file=${snapshotFile} && ${pkgs.coreutils}/bin/sleep 1 + ${pkgs.coreutils}/bin/mkdir -p ${snapshotMount} + ${pkgs.libguestfs-with-appliance}/bin/guestmount -a ${snapshotFile} -m /dev/sda1 --ro ${snapshotMount} + ''; + + afterEverything = pkgs.writeShellScriptBin "afterEverything" '' + set +e + ${pkgs.coreutils}/bin/sleep 10 + ${pkgs.libguestfs-with-appliance}/bin/guestunmount ${snapshotMount} && ${pkgs.coreutils}/bin/sleep 1 + ${pkgs.coreutils}/bin/rm -rf ${snapshotMount} + ${pkgs.libvirt}/bin/virsh blockcommit ${cfg.domainName} vdb --active --verbose --pivot + ${pkgs.coreutils}/bin/rm -f ${snapshotFile} + ''; + + borgmaticConfig = pkgs.writeTextFile { + name = "borgmatic-config"; + text = '' + source_directories: + - ${snapshotMount} + repositories: + - path: ${cfg.repoLocation} + label: ${cfg.domainName} + keep_daily: 7 + keep_weekly: 4 + keep_monthly: 6 + unknown_unencrypted_repo_access_is_ok: true + before_everything: + - ${beforeEverything}/bin/beforeEverything + after_everything: + - ${afterEverything}/bin/afterEverything + ''; + }; +in +{ + options.custom.backups = { + enable = lib.mkOption { + default = false; + type = lib.types.bool; + description = '' + Whether to enable backups of persistent data on this machine. + ''; + }; + + repoLocation = lib.mkOption { + default = "${config.custom.dataDisk.mountPoint}/backups/thecloud-data.borg"; + type = lib.types.str; + description = '' + Location of the Borg repository to back up to. + ''; + }; + + domainName = lib.mkOption { + default = "thecloud"; + type = lib.types.str; + description = '' + The name of the Libvirt domain with the data disk attached. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + environment.systemPackages = with pkgs; [ libguestfs-with-appliance borgbackup ]; + # Converted from: + # https://github.com/borgmatic-collective/borgmatic/tree/84823dfb912db650936e3492f6ead7e0e0d32a0f/sample/systemd + systemd.services.borgmatic = { + description = "borgmatic backup"; + wants = [ "network-online.target" ]; + after = [ "network-online.target" ]; + unitConfig = { + ConditionACPower = true; + }; + serviceConfig = { + Type = "oneshot"; + Nice = 19; + CPUSchedulingPolicy = "batch"; + IOSchedulingClass = "best-effort"; + IOSchedulingPriority = 7; + IOWeight = 100; + Restart = "no"; + LogRateLimitIntervalSec = 0; + }; + preStart = "${pkgs.coreutils}/bin/sleep 1m"; + script = "${pkgs.systemd}/bin/systemd-inhibit --who=\"borgmatic\" --what=\"sleep:shutdown\" --why=\"Prevent interrupting scheduled backup\" ${pkgs.borgmatic}/bin/borgmatic --verbosity -2 --syslog-verbosity 1"; + }; + + environment.etc."borgmatic/config.yaml" = { + source = borgmaticConfig; + }; + + systemd.timers.borgmatic = { + description = "Run borgmatic backup"; + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = "*-*-* 3:00:00"; + Persistent = true; + RandomizedDelaySec = "3h"; + }; + }; + }; +} diff --git a/modules/custom/default.nix b/modules/custom/default.nix index 4cdb9ba..2c8ba2a 100644 --- a/modules/custom/default.nix +++ b/modules/custom/default.nix @@ -1,3 +1,3 @@ { - imports = [ ./terraform-database.nix ./data-disk.nix ./ssh-certificates.nix ./k3s ./disko.nix ]; + imports = [ ./terraform-database.nix ./data-disk.nix ./ssh-certificates.nix ./k3s ./disko.nix ./backups.nix ]; }