From 1b4ed9c3c012f6a0c4db68dbceb3ed3aaf2a079d Mon Sep 17 00:00:00 2001 From: Ladas552 Date: Sat, 8 Aug 2026 04:30:21 +0500 Subject: [PATCH] hetzner post --- content/nix/hetzner.norg | 267 ++++++++++++++++++++++++++++------ flake.lock | 41 +----- flake.nix | 2 +- theme/templates/home-nix.html | 5 +- 4 files changed, 225 insertions(+), 90 deletions(-) diff --git a/content/nix/hetzner.norg b/content/nix/hetzner.norg index 88cc782..5fbd863 100644 --- a/content/nix/hetzner.norg +++ b/content/nix/hetzner.norg @@ -10,24 +10,28 @@ categories: [ vps self-hosting ] -created: 2026-03-14 -draft: true +created: 2026-08-08 +draft: false layout: post version: 1.1.1 @end + +* My Hetzner VPS running NixOS ** TLDR Skip to {*** Setup} if you know what a VPS is and just need a NixOS config that works for Hetzner on ARM. + + Skip to {** The Fun Part} if you are interested in whatever I am running. ** What's a VPS - It's basically a virtual server on someone else big server. VPS are a convenient tool to expose data to the internet, without risks coming from opening ports on your local network. Also you should consider any sort of file system encryption useless on the VPS, so always encrypt sensitive data if any you gonna have on your VPS. + It's basically a virtual server on someone else's big server. VPS are a convenient tool to expose data to the internet, without risks coming from opening ports on your local network. Also you should consider any sort of file system encryption useless on the VPS, so always encrypt sensitive data directly with something like {https://github.com/FiloSottile/age}[age] if you gonna host any on your VPS. - In this example I got a *Hetzner CAX11* with ARM(aarch64) double thread processor, 4GB of RAM and 40GB of storage. All for *5.52$* a month. It's not much, but enough to install nixos, and a few services. + In this example I got a *Hetzner CAX11* with ARM(aarch64) double thread processor, 4GB of RAM and 40GB of storage. All for *$5.52* a month. It's not much, but enough to install nixos, and a few services. *** What are these services? I run: - Minecraft proxy with nginx stream; {https://minecraft.ladas552.me} - - BlueSky PDS for my DID; {https://social.ladas552.me} - - Tangled knot for my git hosting; {https://knot.ladas552.me} - - This and my blogs site are exposed with `caddy` using `file_server` feature. + - {https://tangled.org/tranquil.farm/tranquil-pds}[tranquil-pds] PDS for my DID; {https://social.ladas552.me} + - {https://tangled.org}[Tangled] knot for my git hosting; {https://knot.ladas552.me} + - This blog site is exposed with `caddy` using `file_server` feature. What I couldn't run: - Docker with {https://github.com/tazjin/nixery}[nixery]. It OOMed @@ -43,12 +47,12 @@ version: 1.1.1 I know Cloudflare can have outages, and ill practices but they are: Free, easiest to use, and have the most amount of guides. If you can use something else, like your own DNS provider, you clearly missed the shortcut at the top of this page and wasted your time. **** Rent the VPS - There are free VPS available on Oracle and some government owned servers(depends on your country). But I went with Hetzner because they were the only ones to approve my identity. + There are free VPS available on Oracle and some government owned servers(depends on your country). But I went with Hetzner because they were the only ones to approve my government ID. - So register the Hetzner account, if it doesn't let you, or errors out - Recheck your credentials, and also try another bank for your debit card information. Yes, it can flip you over your bank. In my case it was my second bank that worked. Also depending on your country, it may request your Government ID or Passport. Shitty, I know. But it is how it is sometimes. + So register the Hetzner account, if it doesn't let you, or errors out - Recheck your credentials, and also try another bank for your debit card information. Yes, it can flip you over your bank. In my case it was my second bank that worked. Also depending on your country and bank, it may request your Government ID or Passport. Shitty, I know. But it is how it is sometimes. **** Firewall - Setup a firewall in the Hetzner panel, enable port `22` for you to be able to ssh into it later. And have a IPv4 IP setup if some of your services don't support IPv6, or your IPS provider bans any IPv6 connections in your network, so you end up debugging ssh for a couple of hours until learning that, just because you wanted to cheap out 0.5$ for the static address. Couldn't be me tho. + Setup a firewall in the Hetzner panel, enable port `22` for you to be able to ssh into it later. And have a IPv4 IP setup if some of your services don't support IPv6, or your ISP provider bans any IPv6 connections in your network, so you end up debugging ssh for a couple of hours until learning that, just because you wanted to cheap out 0.5$ for the static address. Couldn't be me tho. *** Setup **** SSH Load into NixOS image, at the time of writing the latest available is 25.11 @@ -58,7 +62,7 @@ version: 1.1.1 +html.height 360 .image ../../assets/hetzner/iso.avif - To SSH into your Hetzner box, get into the console window, The button near `Actions`. And change the password for the root account: `sudo passwd`. After this, you can ssh into it as `ssh root@ip-of-your-box` from any machine, without using crappy console webui. This password won't be saved after the install. After this, you can transfere in your keys, or anything else your installation needs to work. + To SSH into your Hetzner box, get into the console window, The button near `Actions`. And change the password for the root account: `sudo passwd`. After this, you can ssh into it as `ssh root@ip-of-your-box` from any machine, without using crappy console webui. This password won't be saved after the install. After this, you can transfer in your keys, or anything else your installation needs to work. For {https://github.com/Mic92/sops-nix}[sops-nix] users, it's just `scp ./keys.txt root@ip:/root` @@ -105,7 +109,7 @@ version: 1.1.1 Also, one of the reasons I use ARM Hetzner machine, is because the `x86_64-linux` uses legacy boot, so it needs MBR partition, and like hell I know how to set this shit up. I tried different approaches, manual installation, nixos-anywhere. But it just wouldn't find the boot. So here is script for UEFI systems. - Look into my {https://nix.ladas552.me/Impermanence}[Impermanence guide] to understand the ZFS setup, other stuff is just basic Linux stuff and explained in the comments. + Look in my {https://nix.ladas552.me/Impermanence}[Impermanence guide] to understand the ZFS setup, other stuff is just basic Linux stuff and explained in the comments. @code bash # installation script for Hetzner VPS @@ -192,50 +196,52 @@ version: 1.1.1 .image ../../assets/hetzner/firewall.avif ** The Fun Part - Now we can finally run our shit. Well, at least I can show off what I run there. If you don't find minecraft proxy, static site hosting, blueskyPDS, and Tangled git hosting interesting. This is the end of the article for you. Have a great day. + Now we can finally run our shit. Well, at least I can show off what I run there. If you don't find minecraft proxy, static site hosting, tranquil-pds, and Tangled git hosting interesting. This is the end of the article for you. Have a great day. Now for the rest of you, let's roll. -*** Bluesky PDS +*** Tranquil PDS It's one of the ways to get the {https://w3c-ccg.github.io/did-method-web/}[DID] for AT Protocol for some webservices and social media. Tho it also allows me to have a fancy handle on *BlueSky*. So it's good to have if you like to yap yap into the void. - The secrets config is annoying tho, it requires you to setup sops template file, and a bunch of admin creds. - - But getting good defaults was easy, because I stole the config from fellow Kazakhstan citizen {https://sapphic.moe}[SapphoSys], thanks a lot. - - @code nix - { - flake.modules.nixos.bluesky-pds = - { lib, config, ... }: - { + Of course there are other pds services you can try, I for example used tranquil-pds, but stopped because it's dependents on `npm` and it's insecure piece of shit. So I migrated to tranquil-pds. + +**** Migrating a pds + To migrate a pds, you can just: + - create your own `car` file backup of your DID records. `goat repo export ladas552.me` for example. + - backup your master key, this is used to prove that DID migration is done by a cool person who owns the DID. + - setup tranquil-pds, and enter the migration setup, import the car file, enter master key, reaccept pds records. done +**** Setting up tranquil-pds on nixos + It's a simple module on nixos unstable channel, feel free to copy it. It uses sops, and impermanence options. You can just delete thous if you use something else. + @code nix + { config, ... }: { # secrets - sops.secrets."mystuff/bluesky-pdsJWT" = { }; - sops.secrets."mystuff/bluesky-pdsADMIN" = { }; - sops.secrets."mystuff/bluesky-pdsKEY" = { }; - sops.templates."bluesky-pds-secrets".content = '' - PDS_JWT_SECRET="${config.sops.placeholder."mystuff/bluesky-pdsJWT"}" - PDS_ADMIN_PASSWORD="${config.sops.placeholder."mystuff/bluesky-pdsADMIN"}" - PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX="${config.sops.placeholder."mystuff/bluesky-pdsKEY"}" + sops.secrets."mystuff/trJWT" = { }; + sops.secrets."mystuff/trDROP" = { }; + sops.secrets."mystuff/trKEY" = { }; + # create secrets using `openssl rand -base64 48` + sops.templates."tranquil-pds-secrets".content = '' + JWT_SECRET="${config.sops.placeholder."mystuff/trJWT"}" + DPOP_SECRET="${config.sops.placeholder."mystuff/trDROP"}" + MASTER_KEY="${config.sops.placeholder."mystuff/trKEY"}" ''; - # module - services.bluesky-pds = { + # Module + services.tranquil-pds = { enable = true; - goat.enable = true; - pdsadmin.enable = true; + database.createLocally = true; # don't wanna deal with postgress settings = { - PDS_HOSTNAME = "social.ladas552.me"; - PDS_PORT = 3000; - PDS_BLOB_UPLOAD_LIMIT = "200000000"; # 200 MB - PDS_CRAWLERS = lib.concatStringsSep "," [ - "https://bsky.network" - "https://relay.cerulea.blue" - "https://relay.upcloud.world" - "https://atproto.africa" - ]; + server = { + hostname = "social.ladas552.me"; + age_assurance_override = true; + disable_account_verification_gate = true; + banned_words = [ + "emacs" + "guix" + ]; + }; }; environmentFiles = [ - config.sops.templates."bluesky-pds-secrets".path + config.sops.templates."tranquil-pds-secrets".path ]; }; @@ -243,14 +249,181 @@ version: 1.1.1 services.caddy.virtualHosts."social.ladas552.me" = { extraConfig = '' handle { - reverse_proxy http://127.0.0.1:${toString config.services.bluesky-pds.settings.PDS_PORT} + reverse_proxy localhost:3000 + } + ''; + }; + + # persist for Impermanence + custom.imp.root.directories = [ "/var/lib/tranquil-pds" ]; + } + @end + +*** Tangled knot and git hosting + I actually bought a vps exactly for hosting my own repos on tangled. I could use their own `knot1`, but that defeats the purpose of using anything but GitHub, imo. + + Surprisingly straight forward tangled setup. You just need to add the tangled input, import *knot* module for git repos, and/or *spindle* for hosting your own CI. Input looks ugly because tangled isn't recognized by flakes *yet*: `git+https://tangled.org/tangled.org/core` + + @code nix + { config, inputs, ... }: + let + cfg = config.services.tangled.knot; + in + { + imports = [ + # git + inputs.tangled.nixosModules.knot + # UI + # inputs.tangled.nixosModules.appview + # CI + inputs.tangled.nixosModules.spindle + ]; + + # module + services = { + tangled = { + knot = { + enable = true; + gitUser = "git"; + repo.scanPath = "${cfg.stateDir}/repos"; + server = { + listenAddr = "0.0.0.0:3050"; + hostname = "knot.ladas552.me"; + # There are 2 addresses, because the first one is used for tangled's appview to talk with your knot + # and second one is used internally for knot daemon + internalListenAddr = "127.0.0.1:5444"; + # get from your PDS + owner = "did:plc:6ikdlkw64mrjygj6cea62kn4"; # @ladas552.me + }; + }; + # My VPS is too weak for docker containers + spindle = { + enable = true; + server = { + listenAddr = "0.0.0.0:6555"; + hostname = "spindle.ladas552.me"; + # get from your PDS + owner = "did:plc:6ikdlkw64mrjygj6cea62kn4"; + maxJobCount = 1; + }; + pipelines = { + workflowTimeout = "10m"; + microvm.defaultImage = "nixos-aarch64"; + }; + }; + }; + }; + + # Reverse proxy + services.caddy.virtualHosts = { + "knot.ladas552.me".extraConfig = '' + handle { + reverse_proxy http://127.0.0.1:3050 + } + ''; + "spindle.ladas552.me".extraConfig = '' + handle { + reverse_proxy http://127.0.0.1:6555 } ''; }; # persist for Impermanence - custom.imp.root.directories = [ "/var/lib/pds/" ]; + custom.imp.root = { + directories = [ + "/home/git" + ]; + cache.directories = [ + # "/var/lib/containers" + "/var/log/spindle" + "/var/lib/spindle" + ]; + }; + @end + + You know, owning my own fucking repo feels good, I even connect to it by opening ssh only with tailscale. So to push to my knot I use a remote `git@nixwool.taila7a93b.ts.net:did:plc:hi7tbaw6b3gnseqt4fkkiqxb`. the did part is just repo name, but permanent. + +*** Minecraft proxy + Because my vps is smallest thing imaginable, with 4GB of RAM total, and I have a home server, I don't host minecraft server on my vps. But because hetzner is exposed to the internet, it's really easy to setup a proxy for other people to get into my server. + + That sounded way too insecure, allowing random people to enter my home server, but don't worry, I connect proxy to my tailnet, so the server has only one access point, being my vps. Also the proxy only provides access to 1 post, which is Minecraft's iconic `25565`. + + Tho it's more of a relay, than a proxy. Btw, setup has minimum lag or ping, tailscale is pretty good for online games, even if the point of entry is somewhere in Finland. + Here is some code for it using nginx for redirects. Don't ask why it uses both caddy and nginx. + @code nix + services.nginx = { + enable = true; + streamConfig = '' + server { + listen 25565; + proxy_pass 100.74.112.27:25565; + } + server { + listen 25565 udp; + proxy_pass 100.74.112.27:25565; + proxy_timeout 15s; + } + ''; }; - } + # Reverse proxy + services.caddy.virtualHosts = { + "minecraft.ladas552.me" = { + extraConfig = '' + handle { + reverse_proxy http://127.0.0.1:25565 + } + ''; + }; + }; + @end + +**** Minecraft server + You might ask, *"How do I host minecraft server itself tho*, and I may answer this question one day. But I am too lazy, so I will just link this video by a *you all know and love*: + + @embed html +
+ +
+ @end + +*** Site + Now the part that is the most important for you my dear reader, how this page even appears on your tiny screen. Thanks to caddy, it's super easy. Just add the rule redirect in cloudflare dns rules, and expose the page via `file_server`. + + @code nix + services.caddy = { + enable = true; + globalConfig = '' + email me@ladas552.me + ''; + virtualHosts = { + "blog.ladas552.me" = { + extraConfig = '' + root * /var/www/blog + file_server + encode gzip + ''; + }; + "nix.ladas552.me" = { + # All of this below, is because I previously had another site, instead of just adding these posts to my blog site. So yeah, mistakes of the past lead to ugly present. + extraConfig = '' + @posts path_regexp posts ^/posts/(.*)$ + redir @posts https://blog.ladas552.me/nix/{re.posts.1} permanent + handle { + redir https://blog.ladas552.me/nix{uri} permanent + } + ''; + }; + "ladas552.me" = { + extraConfig = ''respond "Blog: https://blog.ladas552.me Nix-Docs: https://nix.ladas552.me Git-Hosting: https://tangled.org/did:plc:6ikdlkw64mrjygj6cea62kn4 GitHub: https://github.com/Ladas552"''; + }; + }; + }; @end +** Wrap it up + No, not wrapping packages, I am not into wrapping systemd services. But wrapping up the whole page. I yapped about: Hetzner shit, arm shit, ATprotolol shit, Minecraft shit, and so on. I hope you find some of this interesting, and have good time with your, so called, `public services`. You also could just open ports if you asked your ISP nicely, but that is human interaction, and if you prefer silently solving your problems, or your country is absolute ass for hosting stuff there, then welcome to the New World of wondering if someone has free reign over your puny little jolly server. + + Thanks for reading. diff --git a/flake.lock b/flake.lock index 3cbf573..181cdcd 100644 --- a/flake.lock +++ b/flake.lock @@ -1,23 +1,5 @@ { "nodes": { - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1731533236, - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "nixpkgs": { "locked": { "lastModified": 1785692966, @@ -49,20 +31,18 @@ }, "norgolith": { "inputs": { - "flake-utils": "flake-utils", "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1785181166, - "narHash": "sha256-wQCtk/raWl8akJxiwnp9pjEutkUkrq1FV7z/3Krsveg=", + "lastModified": 1785955200, + "narHash": "sha256-svc3VVOCTX3rH5XzmhKjihl1eucIIAN2rItC9n27xJ0=", "owner": "norgolith", "repo": "core", - "rev": "34d3add667eb00924e53a44caa6e21d3316fc589", + "rev": "57b79b96a0349e0c92ec7a1f59902fdb385b95f3", "type": "github" }, "original": { "owner": "norgolith", - "ref": "norgolith-v1.2.0", "repo": "core", "type": "github" } @@ -72,21 +52,6 @@ "nixpkgs": "nixpkgs", "norgolith": "norgolith" } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 86485cb..b193ac0 100644 --- a/flake.nix +++ b/flake.nix @@ -3,7 +3,7 @@ inputs = { nixpkgs.url = "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz"; - norgolith.url = "github:norgolith/core/norgolith-v1.2.0"; + norgolith.url = "github:norgolith/core"; }; outputs = diff --git a/theme/templates/home-nix.html b/theme/templates/home-nix.html index 826c862..24a0f6f 100644 --- a/theme/templates/home-nix.html +++ b/theme/templates/home-nix.html @@ -4,7 +4,7 @@ {{ content | safe }}
- {% set latest_posts = collection_nix | sort(attribute="created") | reverse | slice(end=2) %} + {% set latest_posts = collection_nix | sort(attribute="created") | reverse%} {% for post in latest_posts %}

{{ post.title }}

@@ -22,9 +22,6 @@
{% endfor %}
-
< Nix Webring -- 2.51.2