EU + US regions on Hetzner: multi-region SaaS on a budget
Multi-region is a solved problem if you pay a hyperscaler enough. This is the other path: AlertKick now runs an EU and a US region on Hetzner, geo-routed for a few dollars a month, and every piece of plumbing the cloud would have hidden from me had to be built by hand.
Why two regions at all
Two reasons, neither of them latency vanity. European customers increasingly want their monitoring data kept in Europe, a data-residency preference you either meet or lose the deal over. And agents phone home from wherever customers run servers, so a US-hosted agent talking to an EU-only backend pays an ocean crossing on every heartbeat.
Routing users: a Worker, not a load balancer
The browser-facing side is geo-routed by a small Cloudflare Worker. It reads the country Cloudflare already attaches to every request, picks the EU or US origin, and forwards using a resolve override - the request keeps its original hostname (so the reverse proxy and the wildcard TLS certificate still match) but resolves to the chosen region’s frontend. On a 5xx it retries the other region once, which is a perfectly good failover story at this scale.
I priced the alternatives first. Cloudflare’s load balancer product wanted a monthly fee for health checks I could implement in ten lines of Worker. Self-hosted GeoDNS meant running and securing a DNS stack forever. Plain round-robin DNS sends half your users to the wrong continent. The Worker costs almost nothing and the entire routing policy is thirty lines of JavaScript I can read.
Routing agents: deliberately no Cloudflare
The monitoring agents do not go through the Worker at all. They hold persistent WebSocket connections, which you do not want riding through an edge proxy that may recycle them, so they connect straight to regional endpoint hostnames - plain DNS records with multiple A records per region, bypassing the proxy layer entirely.
That split (humans through the edge, machines direct) simplified everything downstream. Browser traffic gets DDoS protection and geo-routing; agent traffic gets connection stability and one less moving part.
The plumbing that broke first
Each of these broke something before I got it right:
Token replication. Each region validates agents against its own database. An agent enrolled in the EU then connecting to the US endpoint presented a token the US region had never heard of. Now every token write - enrol, disable, delete - is followed by a best-effort replication to the peer region, with failures logged and retried on the next mutation rather than failing the user’s request.
Certificate SANs. The agent might connect to any regional endpoint hostname, so the endpoint certificate has to carry every one of those names. The original cert listed only the generic endpoint name; connections to the regional names failed TLS until the SAN list covered them all. Wildcard the regional pattern while you are in there and future regions come free.
Sticky sessions. The backend pods keep some short-lived state node-locally. The reverse proxy pins a client to a pod with a sticky cookie, which works until a request path exists where the second request can land on a different pod than the first. Every one of those paths is a bug you have not found yet. We found ours one at a time; the durable fix is moving shared state into the shared database, and “what happens if the next request hits the other pod” is now a standing review question.
Region-tagged telemetry. Each region’s endpoint tags the metrics it ingests with its own location, and the API fans metric reads out per region and merges. Get this wrong and a host looks down in one region’s view while happily heartbeating into the other.
What it costs
The whole edge layer is within Cloudflare’s free-to-cheap tier. The second region is simply a mirror of the first region’s node types on Hetzner pricing, plus one Worker. The real cost was engineering time on the plumbing above - a few weeks of evenings, most of it spent discovering the failure modes rather than writing the fixes.
The lesson I would hand a past version of myself is that multi-region is a property every future feature must respect, not a feature you finish. Every token, every certificate, every piece of session state, every metric read gets asked “and which region?” forever. Charge yourself for that honestly before you commit - and if you can, make everything region-aware from day one, because retrofitting it is where the weeks went.