From 44c83240634b754f6c8b4ee9289aedbc47820d3a Mon Sep 17 00:00:00 2001 From: ryfrd Date: Wed, 18 Mar 2026 20:32:25 +0000 Subject: [PATCH] . --- config.toml | 14 - content/posts/restic-nixos-hetzner.md | 63 +- content/posts/wuthering-heights.md | 2 +- content/posts/zen-twitch-viewing-script.md | 4 +- layouts/404.html | 6 +- layouts/_default/baseof.html | 2 +- layouts/_default/list.html | 24 +- layouts/_default/search.html | 48 +- layouts/_default/single.html | 25 +- layouts/_default/taxonomy.html | 11 + layouts/_default/term.html | 18 + layouts/index.html | 31 +- layouts/partials/footer.html | 10 +- layouts/partials/head.html | 27 +- layouts/partials/header.html | 21 +- public/404.html | 38 +- .../index.html | 85 +- .../index.html | 50 + .../index.html | 119 ++ public/bread-dough-for-lazy-boys/index.html | 81 +- public/categories/index.html | 107 +- public/categories/index.xml | 347 +++- .../index.html | 59 + public/css/dark.css | 5 +- public/css/light.css | 3 +- public/css/main.css | 122 +- public/css/style.css | 417 ++++ .../index.html | 83 +- .../index.html | 81 +- .../index.html | 79 +- public/index.html | 301 ++- public/index.json | 2 +- public/info/index.html | 75 +- public/js/script.js | 37 + .../index.html | 83 +- .../index.html | 81 +- .../index.html | 83 +- public/multi-user-qtile-fiddling/index.html | 87 +- .../index.html | 76 + .../index.html | 77 +- public/placeholder/index.html | 52 + public/posts/index.html | 280 ++- public/posts/index.xml | 349 +++- public/python-podcast-scripting/index.html | 81 +- .../index.html | 83 +- public/search/index.html | 429 ++-- public/search/index.xml | 347 +++- .../index.html | 125 ++ .../index.html | 81 +- public/sitemap.xml | 150 +- .../index.html | 83 +- public/tags/bash-script-twitch/index.html | 22 +- public/tags/bash-script-twitch/index.xml | 339 +++- public/tags/bash-script/index.html | 50 + public/tags/bash-script/index.xml | 1796 +++++++++++++++++ public/tags/bash/index.html | 122 +- public/tags/bash/index.xml | 349 +++- public/tags/caddy/index.html | 112 +- public/tags/caddy/index.xml | 347 +++- public/tags/chess/index.html | 117 +- public/tags/chess/index.xml | 347 +++- public/tags/cooking/index.html | 112 +- public/tags/cooking/index.xml | 347 +++- public/tags/css/index.html | 112 +- public/tags/css/index.xml | 347 +++- public/tags/docker/index.html | 112 +- public/tags/docker/index.xml | 347 +++- public/tags/emacs/index.html | 112 +- public/tags/emacs/index.xml | 347 +++- public/tags/film/index.html | 112 +- public/tags/film/index.xml | 347 +++- public/tags/home-manager/index.html | 132 +- public/tags/home-manager/index.xml | 347 +++- public/tags/hugo/index.html | 44 + public/tags/hugo/index.xml | 1796 +++++++++++++++++ public/tags/index.html | 232 ++- public/tags/index.xml | 349 +++- public/tags/javascript/index.html | 112 +- public/tags/javascript/index.xml | 347 +++- public/tags/lua/index.html | 112 +- public/tags/lua/index.xml | 347 +++- public/tags/music/index.html | 112 +- public/tags/music/index.xml | 347 +++- public/tags/neovim/index.html | 117 +- public/tags/neovim/index.xml | 347 +++- public/tags/nix-colors/index.html | 117 +- public/tags/nix-colors/index.xml | 347 +++- public/tags/nixos/index.html | 167 +- public/tags/nixos/index.xml | 349 +++- public/tags/podman/index.html | 112 +- public/tags/podman/index.xml | 347 +++- public/tags/python/index.html | 142 +- public/tags/python/index.xml | 347 +++- public/tags/qtile/index.html | 122 +- public/tags/qtile/index.xml | 347 +++- public/tags/restic/index.html | 44 + public/tags/restic/index.xml | 1796 +++++++++++++++++ public/tags/self-hosting/index.html | 112 +- public/tags/self-hosting/index.xml | 347 +++- public/tags/tailscale/index.html | 117 +- public/tags/tailscale/index.xml | 349 +++- public/tags/thing/index.html | 44 + public/tags/thing/index.xml | 1796 +++++++++++++++++ public/tags/twitch/index.html | 44 + public/tags/twitch/index.xml | 1796 +++++++++++++++++ public/tags/wotsit/index.html | 44 + public/tags/wotsit/index.xml | 1796 +++++++++++++++++ .../index.html | 87 +- .../teeny-tiny-bash-fetch-script/index.html | 81 +- .../index.html | 83 +- .../index.html | 83 +- public/translating-docker-to-nix/index.html | 85 +- .../index.html | 83 +- .../index.html | 83 +- public/wuthering-heights-2026/index.html | 83 +- static/css/dark.css | 5 +- static/css/light.css | 3 +- static/css/main.css | 122 +- static/css/style.css | 417 ++++ static/js/script.js | 37 + 120 files changed, 22842 insertions(+), 4673 deletions(-) create mode 100644 layouts/_default/taxonomy.html create mode 100644 layouts/_default/term.html create mode 100644 public/adventures-in-running-headscale-on-nixos/index.html create mode 100644 public/backing-up-nixos-state-with-restic/index.html create mode 100644 public/configuring-a-dynamic-modern-and-minimal-diy-wayland-desktop-environment-with-home-manager/index.html create mode 100644 public/css/style.css create mode 100644 public/js/script.js create mode 100644 public/nginx-reverse-proxy-with-ssl-for-services-running-on-tailscale/index.html create mode 100644 public/placeholder/index.html create mode 100644 public/setting-up-a-lean-mean-hugo-blogging-theme/index.html create mode 100644 public/tags/bash-script/index.html create mode 100644 public/tags/bash-script/index.xml create mode 100644 public/tags/hugo/index.html create mode 100644 public/tags/hugo/index.xml create mode 100644 public/tags/restic/index.html create mode 100644 public/tags/restic/index.xml create mode 100644 public/tags/thing/index.html create mode 100644 public/tags/thing/index.xml create mode 100644 public/tags/twitch/index.html create mode 100644 public/tags/twitch/index.xml create mode 100644 public/tags/wotsit/index.html create mode 100644 public/tags/wotsit/index.xml create mode 100644 static/css/style.css create mode 100644 static/js/script.js diff --git a/config.toml b/config.toml index 6e412f6..e31f593 100644 --- a/config.toml +++ b/config.toml @@ -9,20 +9,6 @@ pygmentsUseClasses = true dark = "auto" highlight = true -[menu] - [[menu.main]] - identifier = "👋" - name = "👋" - title = "👋" - url = "/info/" - weight = 20 - [[menu.main]] - identifier = "🔎" - name = "🔎" - title = "🔎" - url = "/search/" - weight = 20 - [permalinks] posts = "/:title/" diff --git a/content/posts/restic-nixos-hetzner.md b/content/posts/restic-nixos-hetzner.md index 94cb92e..cf72e70 100644 --- a/content/posts/restic-nixos-hetzner.md +++ b/content/posts/restic-nixos-hetzner.md @@ -4,17 +4,24 @@ date: 2026-02-16 tags: - nixos - restic -draft: true +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. -Now I have my config which means I could set up a forgejo instance just how I like it if everything went to pot. +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 on my VPS. +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, ... }: { @@ -24,9 +31,10 @@ This is the method I cooked up for backing up some of those important bits and b user = "root"; passwordFile = "/etc/nixos/secrets/restic"; paths = [ - "${config.services.forgejo.stateDir}" + "/var/lib/important" + "/var/lib/stuff" ]; - repository = "sftp:user@storagebox:/remotelab"; + repository = "sftp:user@storagebox:/payload"; extraOptions = [ "sftp.command='ssh user@storagebox -i /root/.ssh/id_ed25519 -s sftp'" ]; @@ -38,3 +46,48 @@ This is the method I cooked up for backing up some of those important bits and b }; } ``` + +### 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 :) diff --git a/content/posts/wuthering-heights.md b/content/posts/wuthering-heights.md index 7ec911f..43964fb 100644 --- a/content/posts/wuthering-heights.md +++ b/content/posts/wuthering-heights.md @@ -19,7 +19,7 @@ It felt like a set not something the characters actually inahbited. Even when they were banging on the moors they didn't seem to get muddy. This was a problem for me. - Fundamentally, once you got past the visual glitz and knowingly executed tropes it felt completely emotionally empty. -Given that it's a film about the enduring power of passionate undying love this was also a major problem. +Given that this version seems to be about the enduring power of passionate undying love this was also a major problem. ### Things that intrigued me diff --git a/content/posts/zen-twitch-viewing-script.md b/content/posts/zen-twitch-viewing-script.md index 5b355da..51e0217 100644 --- a/content/posts/zen-twitch-viewing-script.md +++ b/content/posts/zen-twitch-viewing-script.md @@ -1,7 +1,9 @@ --- title: Handy script for a more zen twitch experience date: 2025-08-13 -tags: bash, script, twitch +tags: + - bash + - twitch draft: false --- diff --git a/layouts/404.html b/layouts/404.html index 326ef8b..68e9847 100644 --- a/layouts/404.html +++ b/layouts/404.html @@ -1 +1,5 @@ -

uhoh 404 :/

+{{ define "main" }} +

404 - Page not found

+

The page you're looking for doesn't exist.

+

Go home

+{{ end }} diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html index a2f1f55..d1bae5b 100644 --- a/layouts/_default/baseof.html +++ b/layouts/_default/baseof.html @@ -4,6 +4,6 @@ {{- partial "header.html" . -}}
{{ block "main" . }}{{ end }}
- {{ partial "footer.html" . }} + {{- partial "footer.html" . -}} diff --git a/layouts/_default/list.html b/layouts/_default/list.html index 595f249..f68dd35 100644 --- a/layouts/_default/list.html +++ b/layouts/_default/list.html @@ -1,4 +1,20 @@ -{{ define "main" }} {{ range where site.RegularPages "Type" "in" -site.Params.mainSections }} -{{ .Title }} -{{ end }} {{ end }} +{{ define "main" }} +

{{ .Title }}

+ +{{ $years := .Pages.GroupByDate "2006" }} + +{{ end }} diff --git a/layouts/_default/search.html b/layouts/_default/search.html index a458d63..e3be4fc 100644 --- a/layouts/_default/search.html +++ b/layouts/_default/search.html @@ -1,36 +1,24 @@ {{ define "main" }} +

Search

+

Search posts by keyword.

-

This is not a very clever search. It simply looks if the content of a post contains your query.

- - - + -

Alternatively, this is everything.

- -{{ $pages := where site.RegularPages "Type" "posts" }} -{{ $years := $pages.GroupByDate "2006" }} - - + {{ $sorted := sort $tags "count" "desc" }} +
+ {{ range $i, $tag := $sorted }} + {{ if $i }}·{{ end }} + {{ $tag.name }} + {{ end }} +
+ +{{ end }} {{ end }} diff --git a/layouts/_default/single.html b/layouts/_default/single.html index 09e8033..0a4e516 100644 --- a/layouts/_default/single.html +++ b/layouts/_default/single.html @@ -1,15 +1,18 @@ {{ define "main" }} -
-
-

{{ .Title }}

-
- {{- if isset .Params "date" -}} {{ if eq .Lastmod .Date }} -

{{ .Date | time.Format (i18n "post.created") }}

- {{ else }} -

{{ .Lastmod | time.Format (i18n "post.updated") }}

- {{ end }} {{- end -}} -
+
+
+

{{ .Title }}

+ + {{ with .Params.tags }} +
+ {{ range . }} + {{ . }} + {{ end }} +
+ {{ end }}
- {{- .Content -}} +
+ {{ .Content }} +
{{ end }} diff --git a/layouts/_default/taxonomy.html b/layouts/_default/taxonomy.html new file mode 100644 index 0000000..57d065a --- /dev/null +++ b/layouts/_default/taxonomy.html @@ -0,0 +1,11 @@ +{{ define "main" }} +

Tags

+
    + {{ range .Pages }} +
  • + + {{ .Title }} +
  • + {{ end }} +
+{{ end }} diff --git a/layouts/_default/term.html b/layouts/_default/term.html new file mode 100644 index 0000000..cef80a0 --- /dev/null +++ b/layouts/_default/term.html @@ -0,0 +1,18 @@ +{{ define "main" }} +{{ $years := .Pages.GroupByDate "2006" }} +
    + {{ range $years }} +
  • + {{ .Key }} +
      + {{ range .Pages }} +
    • + + {{ .Title }} +
    • + {{ end }} +
    +
  • + {{ end }} +
+{{ end }} diff --git a/layouts/index.html b/layouts/index.html index 20d5767..3d6238d 100644 --- a/layouts/index.html +++ b/layouts/index.html @@ -1,9 +1,26 @@ {{ define "main" }} -{{ range first 5 (where .Site.RegularPages "Type" "in" site.Params.mainSections) }} -
-

{{ .Title }}

-

{{ .Date.Format "Jan 2, 2006" }}

-

{{ .Summary }}

-
-{{ end }} +
+

This is a place to document bits and bobs I've been up to that have interested me. Expect linux and self-hosting tinkering, some novice programming, and maybe the occasional recipe. I tend to be a fool so take anything written here with a pinch of salt :)

+
+ +{{ $posts := where site.RegularPages "Type" "in" site.Params.mainSections }} + +
+ {{ $years := $posts.GroupByDate "2006" }} +
    + {{ range $years }} +
  • + {{ .Key }} +
      + {{ range .Pages }} +
    • + + {{ .Title }} +
    • + {{ end }} +
    +
  • + {{ end }} +
+
{{ end }} diff --git a/layouts/partials/footer.html b/layouts/partials/footer.html index 7fe9603..0198361 100644 --- a/layouts/partials/footer.html +++ b/layouts/partials/footer.html @@ -1,8 +1,8 @@ -
+ diff --git a/layouts/partials/head.html b/layouts/partials/head.html index c856bd2..fe80e37 100644 --- a/layouts/partials/head.html +++ b/layouts/partials/head.html @@ -1,16 +1,19 @@ - - - {{ with .Site.Params.description -}} - - {{ end }} - {{ printf `` ("favicon.ico" | absURL) | safeHTML }} - {{ with .OutputFormats.Get "rss" -}} - {{ printf `` .Rel .MediaType.Type .Permalink $.Site.Title | safeHTML }} - {{ end -}} + + + {{ with .Site.Params.description -}} + + {{ end }} + + {{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ .Title }} - {{ .Site.Title }}{{ end }} - + {{ with .OutputFormats.Get "rss" -}} + {{ printf `` .Rel .MediaType.Type .Permalink $.Site.Title | safeHTML }} + {{ end -}} - - {{ .Title }} + + + diff --git a/layouts/partials/header.html b/layouts/partials/header.html index 3c10d64..6139383 100644 --- a/layouts/partials/header.html +++ b/layouts/partials/header.html @@ -1,14 +1,11 @@ -
+ + diff --git a/public/adventures-in-running-headscale-on-nixos/index.html b/public/adventures-in-running-headscale-on-nixos/index.html new file mode 100644 index 0000000..5ef54df --- /dev/null +++ b/public/adventures-in-running-headscale-on-nixos/index.html @@ -0,0 +1,50 @@ + + + + + + + adventures in running headscale on nixos - James' Blog + + + + + + +
+
+
+

adventures in running headscale on nixos

+ + +
+ + nixos + +
+ +
+
+ +
+
+
+ + diff --git a/public/backing-up-nixos-state-with-restic/index.html b/public/backing-up-nixos-state-with-restic/index.html new file mode 100644 index 0000000..4b39d8b --- /dev/null +++ b/public/backing-up-nixos-state-with-restic/index.html @@ -0,0 +1,119 @@ + + + + + + + Backing up nixos state with restic - James' Blog + + + + + + +
+
+
+

Backing up nixos state with restic

+ + +
+ + nixos + + restic + +
+ +
+
+

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 :)

+ +
+
+
+ + diff --git a/public/bread-dough-for-lazy-boys/index.html b/public/bread-dough-for-lazy-boys/index.html index 896f137..81e211a 100644 --- a/public/bread-dough-for-lazy-boys/index.html +++ b/public/bread-dough-for-lazy-boys/index.html @@ -1,40 +1,41 @@ - - - - - - + + + + + + Bread dough for lazy boys - James' Blog - - Bread dough for lazy boys + + + -