diff --git a/hosts/poseidon/default.nix b/hosts/poseidon/default.nix index e45b02a..46840ba 100644 --- a/hosts/poseidon/default.nix +++ b/hosts/poseidon/default.nix @@ -66,9 +66,9 @@ in websocketPort = 3012; }; - borg-backup = { + restic-backup = { enable = true; - repo = secrets.borg-backup.poseidon-repo; + repo = secrets.restic-backup.poseidon-repo; }; fail2ban = { diff --git a/secrets/default.nix b/secrets/default.nix index 547eb06..59c9049 100644 --- a/secrets/default.nix +++ b/secrets/default.nix @@ -17,6 +17,7 @@ with lib; gandiKey = lib.fileContents ./gandi-api-key.secret; borg-backup = import ./borg-backup { inherit lib; }; + restic-backup = import ./restic-backup { inherit lib; }; matrixEmailConfig = import ./matrix-email-config.nix; }; diff --git a/secrets/restic-backup/default.nix b/secrets/restic-backup/default.nix new file mode 100644 index 0000000..1d6b630 --- /dev/null +++ b/secrets/restic-backup/default.nix @@ -0,0 +1,4 @@ +{ lib }: +{ + poseidon-repo = lib.fileContents ./poseidon-repo.secret; +} diff --git a/secrets/restic-backup/poseidon-repo.secret b/secrets/restic-backup/poseidon-repo.secret new file mode 100644 index 0000000..db082cf Binary files /dev/null and b/secrets/restic-backup/poseidon-repo.secret differ diff --git a/services/default.nix b/services/default.nix index 2904314..4291e78 100644 --- a/services/default.nix +++ b/services/default.nix @@ -20,6 +20,7 @@ ./pipewire.nix ./postgresql-backup.nix ./postgresql.nix + ./restic-backup.nix ./tailscale.nix ./tgv.nix ./transmission.nix diff --git a/services/restic-backup.nix b/services/restic-backup.nix new file mode 100644 index 0000000..701cd97 --- /dev/null +++ b/services/restic-backup.nix @@ -0,0 +1,83 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.my.services.restic-backup; + secrets = config.my.secrets; + excludeArg = with builtins; with pkgs; + "--exclude-file=" + (writeText "excludes.txt" (concatStringsSep "\n" cfg.exclude)); + makePruneOpts = pruneOpts: + attrsets.mapAttrsToList (name: value: "--keep-${name} ${toString value}") pruneOpts; +in { + options.my.services.restic-backup = { + enable = mkEnableOption "Enable Restic backups for this host"; + + repo = mkOption { + type = types.str; + default = null; + example = "/mnt/hdd"; + description = "Restic backup repo"; + + }; + + paths = mkOption { + type = with types; listOf str; + default = [ ]; + example = [ + "/var/lib" + "/home" + ]; + description = "Paths to backup"; + }; + + exclude = mkOption { + type = with types; listOf str; + default = [ ]; + example = [ + # very large paths + "/var/lib/docker" + "/var/lib/systemd" + "/var/lib/libvirt" + + # temporary files created by `cargo` and `go build` + "**/target" + "/home/*/go/bin" + "/home/*/go/pkg" + ]; + description = "Paths to exclude from backup"; + }; + + prune = mkOption { + type = types.attrs; + default = { + daily = 7; + weekly = 4; + monthly = 6; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.restic ]; + + services.restic.backups.backblaze = { + initialize = true; + + paths = cfg.paths; + + repository = cfg.repo; + passwordFile = "/root/restic/password"; + s3CredentialsFile = "/root/restic/creds"; + + extraBackupArgs = [ ] + ++ optional (builtins.length cfg.exclude != 0) excludeArg; + + timerConfig = { + OnCalendar = "daily"; + }; + + pruneOpts = makePruneOpts cfg.prune; + }; + }; +}