Skip to main content

Cutting the last wire

Author
Thornbush
HomeStack Part II - This article is part of a series.
Part 4: This Article

Why
#

There is a difference between infrastructure you control and infrastructure you depend on. For most of the time HomeStack has been running, that line ran through a single service: the commercial tunnel (Cloudflare) that routed all external traffic through someone else’s network before it reached the server at home.

It was convenient. It absorbed attacks, handled certificates, and required almost no configuration. It was also a dependency — on a company’s continued existence, continued pricing, continued policy decisions. Every service in the stack was reachable from the outside only because a third party allowed it to be.

Removing that dependency turned out not to be a single decision. It was seven.

Family
#

Nothing about this week was visible from the outside. All six services stayed reachable. Passwords, photos, files, notes — all accessible the same way they had always been, from anywhere.

What changed was where the traffic went and who controlled the path. Before this week, a request to open the photo library traveled through servers in the United States before reaching the Raspberry Pi in the study. After this week, it goes directly to a small server in Frankfurt — one I rent, configure, and back up myself — and from there through an encrypted tunnel to the Pi.

The difference is not visible. That is the point. Sovereignty should not require accepting a worse experience.

Tech
#

The first step was standing up a new server. Before any migration could happen, that server needed to be hardened from scratch: access locked to SSH keys and a second factor, a firewall that blocks everything except what is explicitly needed, intrusion detection active from the first minute. The lesson from running the Pi is that a server on the public internet attracts automated scanners within seconds of becoming reachable. Eight failed login attempts arrived before the first service was deployed.

The tunnel software (Pangolin) itself took three sessions to get right. The core problem was a chain of proxies: external traffic arrives at the new server, gets forwarded through an encrypted tunnel to the Pi, hits a reverse proxy that routes it to the right service. Each proxy in that chain needs to speak the same protocol as the one before it. Getting that handshake right — particularly the part where the inner proxy needs the hostname to route correctly, not just the IP address — was not documented anywhere in a way that matched the actual behavior of the software.

The debugging pattern that finally worked was the same one that works for most network problems: start at one end, verify what arrives at each step, find where the chain breaks. In this case the break was a single missing field — the server name indication that tells the receiving proxy which site the request is for. Once that was in place, all six services came up within minutes.

The management layer followed a different path. The VPN that allows administering the stack from outside the home network had been running on a commercial coordination service (Tailscale). Replacing it with a self-hosted equivalent (Headscale) meant migrating four devices one at a time — laptop first, phones, then the Pi, then the server itself last — keeping the commercial fallback active until every device was confirmed working on the new system. The order mattered: the server hosts its own coordination service, so migrating it last meant there was always a working fallback if something went wrong.

The DNS migration came third. Moving the domain away from the last external provider exposed a behavior that was not documented: the new provider does not activate DNS records until its own nameservers are set as authoritative for the domain. Records in the dashboard, records not visible on the nameservers — an hour of debugging that ended with a single configuration change and everything resolving correctly within minutes.

The certificate infrastructure changed alongside the DNS. The old setup used a wildcard certificate that required API access to the DNS provider to prove domain ownership. The new setup uses a different proof method that requires no external API at all — each subdomain gets its own certificate, renewed automatically by the server responding to a challenge on port 443. Simpler, fewer dependencies, and the certificates renewed themselves correctly within thirty seconds of the configuration change.

Project
#

WS1 started as a workstream with four steps. By the time it closed, it had seven. Each step revealed the next dependency. The original plan did not account for the certificate infrastructure being coupled to the DNS provider, or for the DNS provider having an activation mechanism that differed from every other DNS service used before.

That is not a planning failure. A dependency you cannot see until you are close enough to touch it is not a dependency you could have planned around. The correct response is to document it when you find it — so the next migration, on the next system, does not repeat the same hour of debugging.

Cloudflare is no longer in the path. No DNS, no tunnel, no certificate infrastructure, no API token anywhere in the configuration. The last reference was removed from a configuration file that turned out to have been dead code for months — the setting was there, the plugin was built in, and none of it had ever been used.

That is a clean exit.

HomeStack Part II - This article is part of a series.
Part 4: This Article