119 lines
10 KiB
HTML
119 lines
10 KiB
HTML
<!doctype html>
|
|
<html lang="en"><head><script src="/livereload.js?mindelay=10&v=2&port=1313&path=livereload" data-no-instant defer></script>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
<link rel="canonical" href="http://localhost:1313/backing-up-nixos-state-with-restic/" />
|
|
<title>Backing up nixos state with restic - James' Blog</title>
|
|
|
|
<link rel="stylesheet" href="/css/style.css">
|
|
|
|
<script>
|
|
document.documentElement.setAttribute('data-theme', 'dark');
|
|
</script>
|
|
</head>
|
|
<body><header class="site-header">
|
|
<nav>
|
|
<a href="http://localhost:1313/" class="home">~</a>
|
|
<div class="nav-links">
|
|
|
|
<a href="/search">/</a>
|
|
</div>
|
|
</nav>
|
|
</header>
|
|
<main id="content">
|
|
<article class="post">
|
|
<header>
|
|
<h1>Backing up nixos state with restic</h1>
|
|
<time>Feb 16, 2026</time>
|
|
|
|
<div class="tags">
|
|
|
|
<a href="/tags/nixos">nixos</a>
|
|
|
|
<a href="/tags/restic">restic</a>
|
|
|
|
</div>
|
|
|
|
</header>
|
|
<div class="content">
|
|
<p>I’m writing this so I can hopefully remember what I did in six months.</p>
|
|
<p>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.</p>
|
|
<p>This is the method I cooked up for backing up some of those important bits and bob from my VPS.</p>
|
|
<h3 id="restic">Restic</h3>
|
|
<p><a href="https://restic.net/">Restic</a> 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 <a href="https://search.nixos.org/options?channel=unstable&query=restic">available nixos options</a>, I came up with this little config.
|
|
It sets up a restic job to be run daily by root backing up some of <code>/var/lib</code> over sftp to the hetzner storage box (I’ve put in placeholder values).</p>
|
|
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="p">{</span> <span class="n">config</span><span class="o">,</span> <span class="o">...</span> <span class="p">}:</span> <span class="p">{</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="n">services</span><span class="o">.</span><span class="n">restic</span> <span class="o">=</span> <span class="p">{</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="n">backups</span><span class="o">.</span><span class="s2">"hetzner-storage-box"</span> <span class="o">=</span> <span class="p">{</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="n">initialize</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="n">user</span> <span class="o">=</span> <span class="s2">"root"</span><span class="p">;</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="n">passwordFile</span> <span class="o">=</span> <span class="s2">"/etc/nixos/secrets/restic"</span><span class="p">;</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="n">paths</span> <span class="o">=</span> <span class="p">[</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="s2">"/var/lib/important"</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="s2">"/var/lib/stuff"</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="p">];</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="n">repository</span> <span class="o">=</span> <span class="s2">"sftp:user@storagebox:/payload"</span><span class="p">;</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="n">extraOptions</span> <span class="o">=</span> <span class="p">[</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="s2">"sftp.command='ssh user@storagebox -i /root/.ssh/id_ed25519 -s sftp'"</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="p">];</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="n">timerConfig</span> <span class="o">=</span> <span class="p">{</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="n">OnCalendar</span> <span class="o">=</span> <span class="s2">"daily"</span><span class="p">;</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="n">Persistent</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="p">};</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="p">};</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="p">};</span>
|
|
</span></span><span class="line"><span class="cl"><span class="p">}</span>
|
|
</span></span></code></pre></div><h3 id="security-concerns">Security concerns</h3>
|
|
<p>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 <code>/etc/nixos/secrets/restic</code>.</p>
|
|
<p>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 <a href="https://restic.readthedocs.io/en/stable/080_examples.html#full-backup-without-root">here</a></p>
|
|
<p>The second issue is extremely solvable with something like <a href="https://github.com/Mic92/sops-nix">sops-nix</a> which I really do intend to setup at some point I promise!</p>
|
|
<h3 id="bonus-notifications">Bonus: notifications</h3>
|
|
<p>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 <a href="https://ntfy.sh">ntfy</a> server with an uh oh pay attention message.</p>
|
|
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"> <span class="n">systemd</span><span class="o">.</span><span class="n">services</span><span class="o">.</span><span class="n">restic-backups-vps-storage-box</span> <span class="o">=</span> <span class="p">{</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="n">wantedBy</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">"multi-user.target"</span> <span class="p">];</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="n">unitConfig</span> <span class="o">=</span> <span class="p">{</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="n">OnFailure</span> <span class="o">=</span> <span class="s2">"restic-backups-failure-notify.service"</span><span class="p">;</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="p">};</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="p">};</span>
|
|
</span></span><span class="line"><span class="cl">
|
|
</span></span><span class="line"><span class="cl"> <span class="n">systemd</span><span class="o">.</span><span class="n">services</span><span class="o">.</span><span class="n">restic-backups-failure-notify</span> <span class="o">=</span> <span class="p">{</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="n">description</span> <span class="o">=</span> <span class="s2">"Notify on restic backup failure"</span><span class="p">;</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="n">serviceConfig</span> <span class="o">=</span> <span class="p">{</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="n">Type</span> <span class="o">=</span> <span class="s2">"oneshot"</span><span class="p">;</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="n">ExecStart</span> <span class="o">=</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="s2">"</span><span class="si">${</span><span class="n">pkgs</span><span class="o">.</span><span class="n">curl</span><span class="si">}</span><span class="s2">/bin/curl -s -X POST https://ntfy.sh/restic-backups-topic "</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="s2">"-d 'Restic backup from VPS to storage box failed!' "</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="s2">"-H 'Title: Backup Failed' "</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="s2">"-H 'Priority: high' "</span><span class="p">;</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="n">User</span> <span class="o">=</span> <span class="s2">"root"</span><span class="p">;</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="p">};</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="p">};</span>
|
|
</span></span></code></pre></div><p>Thanks for reading :)</p>
|
|
|
|
</div>
|
|
</article>
|
|
</main><footer class="site-footer">
|
|
<small>
|
|
<a href="/index.xml">RSS</a>
|
|
·
|
|
<a href="https://gohugo.io">Hugo</a>
|
|
</small>
|
|
<script src="/js/script.js"></script>
|
|
</footer>
|
|
</body>
|
|
</html>
|