blog/content/posts/restic-nixos-hetzner.md
2026-03-18 20:32:25 +00:00

3.7 KiB

title date tags draft
Backing up nixos state with restic 2026-02-16
nixos
restic
false

I'm writing this so I can hopefully remember what I did in six months.

As hard as you try to eliminate all state from your computing life with nixos, the fact remains that you can't get rid of all of it. For example, I run forgejo on my VPS. I have my config which means I could set up a forgejo instance just how I like it if everything went to pot. But that wouldn't bring back any of the repos I had there previously.

This is the method I cooked up for backing up some of those important bits and bob from my VPS.

Restic

Restic is a project which facilitates the encrypted, deduplicated backing up of your data to SFTP, S3, and various other cloud providers. My backup target is a hetzner storage box which is compatible with SFTP so that's the route I chose. After browsing the available nixos options, I came up with this little config. It sets up a restic job to be run daily by root backing up some of /var/lib over sftp to the hetzner storage box (I've put in placeholder values).

{ config, ... }: {
  services.restic = {
    backups."hetzner-storage-box" = {
      initialize = true;
      user = "root";
      passwordFile = "/etc/nixos/secrets/restic";
      paths = [
        "/var/lib/important"
        "/var/lib/stuff"
      ];
      repository = "sftp:user@storagebox:/payload";
      extraOptions = [
        "sftp.command='ssh user@storagebox -i /root/.ssh/id_ed25519 -s sftp'"
      ];
      timerConfig = {
        OnCalendar = "daily";
        Persistent = true;
      };
    };
  };
}

Security concerns

The eagle-eyed among you will have noticed some GAPING security flaws in this setup. Firstly, my root user needs passwordless ssh access to the storage box. Secondly, the password used to encrypt the backup is sitting in plaintext on my server at /etc/nixos/secrets/restic.

The first issue seems a little tricky to solve. As far as I know there's no way round the passwordless detail if you want an automated backup. It would be better to run the backup as a less privileged user that still has permissions to the stuff you want to backup. I'm not sure how to do that though and the whole thing seemed sufficiently complicated alraedy for me. I found something about it in the docs here

The second issue is extremely solvable with something like sops-nix which I really do intend to setup at some point I promise!

Bonus: notifications

Seeing as these backups are going to be chugging away in the background in the middle of the night, it would be useful to be notified if they went wrong. The nixos service sets up a systemd service for the restic job. We can piggyback off this one with another systemd service which runs when the restic service fails. This new service simply curls my ntfy server with an uh oh pay attention message.

  systemd.services.restic-backups-vps-storage-box = {
    wantedBy = [ "multi-user.target" ];
    unitConfig = {
      OnFailure = "restic-backups-failure-notify.service";
    };
  };

  systemd.services.restic-backups-failure-notify = {
    description = "Notify on restic backup failure";
    serviceConfig = {
      Type = "oneshot";
      ExecStart =
        "${pkgs.curl}/bin/curl -s -X POST https://ntfy.sh/restic-backups-topic "
        + "-d 'Restic backup from VPS to storage box failed!' "
        + "-H 'Title: Backup Failed' "
        + "-H 'Priority: high' ";
      User = "root";
    };
  };

Thanks for reading :)