A service reference that never breaks, even before you turn on private networking
The usual advice for service-to-service calls is “use the internal hostname, not the public one” — reasonable, until the two services end up on different hosts, different providers, or different regions, and the internal hostname stops resolving. Most platforms solve this by making you think about topology up front. Railyard’s approach is to make the reference resolve correctly regardless of topology, and change what’s underneath it as the topology changes.
Three cases, one reference
Every app gets <name>.railyard.internal, and what that name actually resolves to depends entirely on where the other app runs relative to it, not on anything the calling app has to know:
- Same server: it’s a Docker network alias — resolves straight to the container on its own port. No setup, no network to configure.
- Different server, private networking off: the reference still works — it just isn’t private. It resolves to the app’s normal public
https://address instead of failing outright. A reference between two apps on different servers degrades to “works, but over the public internet,” rather than breaking. - Different server, private networking on: every server on the team joins an encrypted WireGuard mesh (
10.210.0.0/16), and the same name now resolves to wherever the app actually runs, across providers and regions, routed over the mesh instead of the public internet.
The part worth sitting with: an app referencing ${{api.RAILYARD_PRIVATE_URL}} doesn’t change when any of this changes. Move api to a different server, attach a server from a different cloud provider entirely, turn private networking on for the first time — the reference keeps resolving, just through a different mechanism each time.
Why graceful degradation beats a hard requirement
The alternative design — private networking is mandatory, or references simply fail until it’s configured — would be more “correct” in some abstract sense and worse in practice: a small team on one server never needs cross-server networking at all, and shouldn’t have to configure a WireGuard mesh to deploy two apps that happen to live in the same place. Making the fallback be “still works, over public HTTPS” instead of “doesn’t work until you set this up” means the same app configuration is valid on day one with one server and still valid after scaling to five servers across two providers — nothing to rewrite when the topology grows.
What turning it on actually changes
Servers → Private network → Turn on (admin-only) is the one action: every server already on the team joins the mesh, and containers already running pick up the new resolver on their next deploy or restart — not instantly, which matters if a reference is latency-sensitive and currently resolving over the public internet. From that point, every server the team adds later — on any provider, in any region — joins the same mesh automatically, since the mesh is a property of the team, not something re-configured per server.
Full reference: Networking docs, Variables and secrets.