93 lines
3.7 KiB
Markdown
93 lines
3.7 KiB
Markdown
---
|
|
title: Backing up nixos state with restic
|
|
date: 2026-02-16
|
|
tags:
|
|
- nixos
|
|
- restic
|
|
draft: 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](https://restic.net/) 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](https://search.nixos.org/options?channel=unstable&query=restic), 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).
|
|
|
|
```nix
|
|
{ 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](https://restic.readthedocs.io/en/stable/080_examples.html#full-backup-without-root)
|
|
|
|
The second issue is extremely solvable with something like [sops-nix](https://github.com/Mic92/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](https://ntfy.sh) server with an uh oh pay attention message.
|
|
|
|
```nix
|
|
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 :)
|