feat: scaffold netcup vps host (disko/vda, static net, tailscale, caddy)

- new nixosConfigurations.vps: UEFI systemd-boot, disko on /dev/vda
- static IPv4 159.195.64.117/22 gw .1 (+ IPv6), eth0 pinned, public DNS
- tailscale via headscale + sops authkey (secrets/vps.yaml, own host key)
- caddy public reverse proxy: audiobookshelf.mgaction.town -> jupiter tailnet:8000
- shared common.nix (user/ssh/nix) ; .sops.yaml per-host rules
This commit is contained in:
erik
2026-07-13 01:01:53 +02:00
parent 25c6982cea
commit 946c44f1d3
8 changed files with 223 additions and 6 deletions
+64
View File
@@ -0,0 +1,64 @@
{ config, pkgs, lib, ... }:
# netcup VPS (UEFI, /dev/vda). Public reverse proxy + tailnet node.
{
imports = [
./hardware-configuration.nix
./disk-config.nix # disko: vda partitions + filesystems
./secrets.nix # sops-nix: tailscale authkey
../common.nix # shared user / ssh / nix settings
];
# ---- Boot (UEFI) ----
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
# Root is on the virtio disk — pin these so stage-1 mounts it regardless of
# what nixos-generate-config detects in the installer.
boot.initrd.availableKernelModules = [ "virtio_pci" "virtio_blk" "virtio_scsi" ];
networking.hostName = "vps";
# ---- Static networking (netcup) ----
# No LAN fallback: get this right or the box is unreachable (use netcup's
# VNC console / rescue system to fix). Values captured from the running VPS.
networking.useDHCP = false;
networking.usePredictableInterfaceNames = false; # keep the NIC named eth0
networking.interfaces.eth0 = {
ipv4.addresses = [ { address = "159.195.64.117"; prefixLength = 22; } ];
ipv6.addresses = [ { address = "2a0a:4cc0:c2:19e1:44b4:8dff:fe4d:c7d7"; prefixLength = 64; } ];
};
networking.defaultGateway = { address = "159.195.64.1"; interface = "eth0"; };
# netcup IPv6 gateway is conventionally fe80::1 — VERIFY with `ip -6 route`
# on the running VPS; wrong v6 gw won't break v4 reachability.
networking.defaultGateway6 = { address = "fe80::1"; interface = "eth0"; };
# Public resolvers for early boot; tailscale MagicDNS overrides once up.
networking.nameservers = [ "9.9.9.9" "1.1.1.1" "2620:fe::fe" ];
networking.firewall = {
enable = true;
allowedTCPPorts = [ 22 80 443 ];
trustedInterfaces = [ "tailscale0" ]; # reach admin services over the tailnet
};
# ---- Tailscale (via headscale) ----
services.tailscale = {
enable = true;
authKeyFile = config.sops.secrets.tailscale_authkey.path;
extraUpFlags = [ "--login-server=https://vpn.mgaction.town" ];
};
# ---- Public reverse proxy ----
# Caddy gets automatic public HTTPS (Let's Encrypt) for real domains.
# Proxies to jupiter's audiobookshelf over the tailnet (MagicDNS name).
# Add one block per public service. Needs a public A record -> this VPS IP
# and ports 80/443 open (they are, above).
services.caddy = {
enable = true;
virtualHosts."audiobookshelf.mgaction.town".extraConfig = ''
reverse_proxy http://jupiter.hosts.mgaction.town:8000
'';
# TODO: port your other VPS services' vhosts here before deploying.
};
system.stateVersion = "26.05"; # set at install time; do NOT bump on upgrades
}
+37
View File
@@ -0,0 +1,37 @@
{ ... }:
# Declarative disk layout (disko) for the netcup VPS.
# UEFI: GPT with an ESP + ext4 root on the single virtio disk.
#
# ⚠️ /dev/vda is WIPED on install — this destroys everything currently on the
# VPS (docker containers, data, existing caddy config). Back up / port your
# services into NixOS BEFORE running nixos-anywhere.
{
disko.devices.disk.vps = {
type = "disk";
device = "/dev/vda"; # netcup virtio disk (256G)
content = {
type = "gpt";
partitions = {
ESP = {
size = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
}
+23
View File
@@ -0,0 +1,23 @@
# PLACEHOLDER — replace on install.
#
# disko (disk-config.nix) owns the filesystems, so this file only carries
# kernel modules + platform. nixos-anywhere regenerates it via:
# nixos-generate-config --no-filesystems
# Keep the imports/kernel-module lines; the virtio modules are pinned in
# configuration.nix so root mounts even if the generator misses them.
{ config, lib, pkgs, modulesPath, ... }:
{
imports = [ (modulesPath + "/profiles/qemu-guest.nix") ];
boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "virtio_pci" "virtio_blk" "sr_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
# NO fileSystems here — disko defines "/" and "/boot".
swapDevices = [ ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
}
+12
View File
@@ -0,0 +1,12 @@
{ config, ... }:
# sops-nix wiring for the VPS. Encrypted values live in ../secrets/vps.yaml,
# decrypted with the VPS's own SSH host key (recipient in ../.sops.yaml).
# The host key is pre-generated on the laptop and shipped at install
# (nixos-anywhere --extra-files -> /etc/ssh/ssh_host_ed25519_key).
{
sops.defaultSopsFile = ../secrets/vps.yaml;
sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
sops.secrets.tailscale_authkey = { };
}