Infrastructure
Zero-Ingress Deployment Platform
The home server could run things, but nothing could reach it. This is the layer that made it publishable: a real domain on real HTTPS, served out of a cupboard, without opening a single port on the home router. Every connection into the network is one the server itself started.
01 — The problem
What was actually happening
The usual way to host from home is to forward a port on the router and point DNS at your IP. It works, and it is also how home networks get found. A forwarded port is a permanent invitation published to every scanner on the internet, and it puts the machine — and everything else on that network — one software bug away from being someone else's.
- Port forwarding advertises your home IP address and hands attackers a fixed target.
- Exposing SSH to the internet means accepting a constant background of credential-stuffing attempts.
- A residential IP changes, so DNS pointing at it needs babysitting or a dynamic-DNS workaround.
- Anything given deploy access to the server can usually reach far more than the one project it needs.
02 — The solution
What I built, and why
Invert the direction of every connection. Instead of the internet reaching in, the server reaches out and holds the door open from the inside — so the router never needs an inbound rule and the home address is never published. Administration moves off the public internet entirely and onto a private network.
- Cloudflare Tunnel: the server opens an outbound connection to Cloudflare, which terminates TLS at the edge and relays traffic back down it. The origin address stays private.
- Tailscale as the administration plane — SSH is bound to the VPN interface, so the LAN address refuses connections outright.
- The tailnet runs default-deny: a policy that grants only SSH and the music server, so nothing else on the box is reachable even from inside the VPN.
- nginx in front of the application on loopback, adding cache rules for hashed build assets and the usual security headers.
- pm2 under systemd for process supervision, so the application returns on its own after a power cut.
- Deployment pulls from a private repository using a read-only key scoped to that one repository — a compromised server cannot reach anything else in the account.
- ufw default-deny, fail2ban on the SSH jail, and unattended security upgrades underneath all of it.
03 — Process
How the work was sequenced
- 01
Decide what must never be true
Started from constraints rather than components: no inbound rules on the router, no administration reachable from the public internet, and no credential on the server that unlocks more than the single project it serves.
- 02
Move administration off the internet
Brought the machine onto a private VPN, then bound SSH to that interface and narrowed the firewall to match. Verified the VPN path worked before removing the local one, because the ordering is the difference between hardening a machine and losing it.
- 03
Build the serving path inward
Application on loopback, reverse proxy on loopback in front of it, and only then the tunnel — so that at no point in the build was anything listening on an address the network could reach.
- 04
Separate the deploy identity
Generated a key on the server itself and registered it as read-only against one repository, so the credential that lives on the exposed machine is the least useful one available.
- 05
Audit the network instead of trusting it
The router is an ISP gateway with no local administration interface, so rather than reading a settings page I queried its UPnP service directly and enumerated the live port-mapping table to confirm what was actually open.
- 06
Pull the power
Rebooted the machine and timed the recovery rather than trusting that services were marked as enabled. Two of them were not configured the way the status output implied, which only surfaced because of the reboot.
04 — Technology
The stack, as decisions
Edge & delivery
Private network
Runtime
Delivery
05 — Results
What it changed
Zero
Inbound ports open on the router, verified against its live mapping table
~100s
From cold reboot to serving traffic again, with nobody at the keyboard
One repo
The blast radius of the deploy credential on the server
The security of this setup comes from the direction connections travel, not from the amount of configuration in it — once every connection is outbound, a whole category of attack simply has nowhere to land. The other lesson was cheaper to learn here than in production: a service marked enabled is not a service that starts. Rebooting on purpose, while nothing depended on the machine yet, found two problems that would otherwise have waited for a power cut to appear.
Have a problem worth solving?
Tell me what the process looks like today and where it breaks. If technology is the right answer, I will tell you what I would build — and if it is not, I will tell you that too.