Status
Accepted (2026-06-06)
Context
mson.sh comprises static sites (portfolio, docs site) and dynamic services. The first dynamic service is self-hosted web analytics, likely Umami, running on the lab’s Talos Kubernetes cluster. Lab-hosted services need public reachability under mson.sh hostnames.
Four constraints drive the decision:
- The home IP must never appear in public DNS or be directly reachable.
- Cloudflare must stay out of the serving path (see Alternatives Considered).
- Monthly cost stays low.
- Analytics downtime is acceptable. The tracking snippet loads asynchronously and fails silently, so visitors never see an outage.
Decision
A small VPS acts as the public edge running a reverse proxy: a Hetzner CAX11 (2 vCPU ARM, 4 GB, €4.49/mo, April 2026 pricing).
A WireGuard tunnel connects the VPS to the lab. The lab initiates the tunnel outbound with persistent keepalive, so no inbound ports open at home.
TLS terminates at the edge, or passes through via proxy protocol to terminate in-cluster. Either way no external provider sees plaintext.
The edge is managed as IaC (OpenTofu and Ansible) like the rest of the lab. The static sites stay off this serving path.
No CDN initially. Analytics ingestion gains nothing from a CDN, since beacon POSTs are uncacheable. If global asset delivery later matters, bunny.net is the candidate ($0.01/GB EU/NA, $1/mo minimum, a small European company).
Consequences
- Full ownership of the serving path, end-to-end TLS, and support for arbitrary TCP and UDP.
- One VPS to patch and monitor.
- DDoS protection is limited to Hetzner’s network-level protection. This is acceptable: an attack on the edge takes down lab services (tolerable), while the static sites stay up independently and the home connection is untouched.
- Worst-case running cost is roughly €5.50/mo (the €4.49 VPS plus the optional $1 CDN minimum).
- The first migration onto this path, analytics, is low-stakes and exercises ingress, TLS, and DNS for every later lab-hosted workload.
Alternatives Considered
Cloudflare Tunnel. An outbound-only connector that is free, hides the home IP, absorbs DDoS well, and has the lowest setup friction. Rejected: Cloudflare terminates TLS and reads plaintext for every request, the DNS zone must move to Cloudflare, and it concentrates the serving path on a provider that already carries a large share of the web. It is also HTTP-centric and awkward for arbitrary TCP and UDP.
Direct port-forward on the VyOS edge (DNAT 443 plus dynamic DNS). Free, with no third parties. Rejected: it puts the home IP in public DNS where it is geolocatable, and a DDoS lands on the residential connection, taking down the whole home network. Residential IP reputation can also trip security scanners.
Tailscale Funnel.
Serves only on *.ts.net hostnames with no custom domains, so it cannot serve mson.sh.
Fine for private access to lab dashboards, but not viable for public services.
Running services on the VPS itself, with no lab in the serving path. Removes the WireGuard hop and the lab dependency. Rejected as the default: the lab’s Talos cluster already provides orchestration, storage, and monitoring, and duplicating that on a 4 GB pet VPS scales poorly past the first service.
Oracle Cloud free tier as the edge (4 ARM OCPUs, 24 GB, free). Generous and free. Rejected for the always-on edge role: Oracle documents that idle Always Free instances may be reclaimed, and a lightly loaded edge proxy can sit below its idle thresholds (95th-percentile CPU under 20% over 7 days). A paid Hetzner box carries no such availability asterisk. It may still host other roles, such as the runners manager.