summaryrefslogtreecommitdiff
path: root/hostnix
diff options
context:
space:
mode:
authorJoe Mou <dev@mou.fo>2025-07-01 13:27:56 -0400
committerJoe Mou <dev@mou.fo>2025-07-01 17:46:29 -0400
commit3c87b48fdfb20f70b57db81dd166cb4cd1affabf (patch)
tree8fb1b490553d582fbde02c292faeb22e5097bd7a /hostnix
parent411998928e7a8bf52f65bed3f0b434671daff909 (diff)
Unattended local backups
Needs /var/secrets/restic to be manually provisioned. Based on ~/.dotfiles/restic/run, as a starting point. Backing up /srv/Attic is huge (100s of GBs), redundant (same hard drive), and slow (hours). It probably makes sense to mirror it instead. The repo password is stored on the same hard drive in plaintext, which means our repo is not secure at rest. This is a bigger issue with the initial setup without full disk encryption, so we choose not to address it; however, this does expose all backups whereas previously just this server was exposed. The most important remaining tasks are to mirror the restic backups remotely, and to automate on a timer.
Diffstat (limited to 'hostnix')
-rw-r--r--hostnix/elmo/backup.nix61
-rw-r--r--hostnix/elmo/configuration.nix1
2 files changed, 62 insertions, 0 deletions
diff --git a/hostnix/elmo/backup.nix b/hostnix/elmo/backup.nix
new file mode 100644
index 0000000..edba6b9
--- /dev/null
+++ b/hostnix/elmo/backup.nix
@@ -0,0 +1,61 @@
+{ lib, ... }:
+
+# TODO consistent btrfs snapshots?
+# TODO dump Home Assistant? Postgres?
+# TODO explicit backup blacklist for /srv and /var? can be a separate cron
+
+{
+ services.restic.backups.local = {
+ # The repo file permissions and our exclude file assume our user.
+ user = "joe";
+ repository = "/srv/restic/repo";
+ paths = [
+ # Same as ~/.dotfiles/restic/run
+ "/etc"
+ "/home"
+ "/root"
+ "/var/home"
+ "/var/spool/cron"
+ "/var/www"
+
+ "/srv/git"
+ "/var/lib"
+ "/var/secrets"
+ ];
+ # The restic repo is not secure at rest because our password is colocated.
+ passwordFile = "%d/password";
+ # Same as ~/.dotfiles/restic/run
+ extraBackupArgs = [
+ "--one-file-system"
+ "--exclude-file=/home/joe/.dotfiles/restic/exclude"
+ "--exclude-caches"
+ ];
+ backupPrepareCommand = let ls-lR = [
+ "/srv/media"
+ "/var/lib/acme"
+ "/var/secrets"
+ ];
+ in
+ ''
+ ls -lR ${lib.concatStringsSep " " ls-lR} > ~/.dotfiles/restic/errata/ls-lR.excluded
+ '';
+ # The wrapper would not be able to use RESTIC_PASSWORD_FILE from a systemd
+ # credential.
+ createWrapper = false;
+ timerConfig = null; # TODO daily?
+ };
+
+ systemd.services.restic-backups-local = {
+ serviceConfig = {
+ LoadCredential = [ "password:/var/secrets/restic" ];
+ AmbientCapabilities = [ "CAP_DAC_READ_SEARCH" ];
+ };
+ };
+
+ # TODO restic-sync to spanommers
+
+ # TODO mirror?
+ # - /srv/Attic (split into archive/mirror and backup/adhoc?)
+ # - /srv/from-spanommers (move to /srv/Attic/Backups?)
+ # - /srv/syncthing (or configure spanommers with syncthing?)
+}
diff --git a/hostnix/elmo/configuration.nix b/hostnix/elmo/configuration.nix
index 0bdda0d..dee01be 100644
--- a/hostnix/elmo/configuration.nix
+++ b/hostnix/elmo/configuration.nix
@@ -3,6 +3,7 @@
{
imports = [
./acme.nix
+ ./backup.nix
./dns.nix
./dyndns.nix
./email.nix