On-demand Railway demos: booting a whole stack on the first click
An always-on gateway that boots a Railway project's whole stack on the first visit and stops it once idle — so demos only run when someone's using them.

I run a handful of portfolio projects on Railway, but they're only used when someone clicks through from here to try them. Paying to keep several full stacks — apps, workers, and databases — running around the clock for the occasional visit made no sense. Railway's built-in serverless sleeps a lone web service well enough, but it leaves the backends and databases humming. So I built a small always-on gateway that boots a project's entire stack the moment someone visits, shows a loading screen while it wakes, and shuts the whole thing down once it's been idle for a few minutes.
What it does
The gateway is the single front door for every demo. A request comes in, it works out which project the domain belongs to, and if that stack is asleep it starts every service — databases first — waits until the app is actually responding, then forwards the request. Every request resets an idle timer; five minutes after the last one, it stops the whole stack again. There's also a command-line tool and a keyboard-driven terminal UI: sync projects straight from a Railway account, start or stop a stack, pause a project so it stays up during an active demo, and drill into per-service status.
How it's built
It's a small Node and TypeScript app talking to Railway's GraphQL API. Bringing a service up is a deploy trigger; taking it down was the interesting part — Railway has no true 'stop', so the tool uses the Remove action (the same one the dashboard uses) to actually halt a deployment and stop compute billing, while volumes and their data stay put. Routing is by hostname: a single wildcard domain points at the gateway, and a config file — generated straight from the Railway account — maps each subdomain to a project and to the internal URL the gateway forwards to.
Details that bit me
Two things were easy to get wrong. First, activity detection only works because the gateway sits inline — traffic has to flow through it, so the demo domains point at the gateway rather than at the projects directly. Second, the gateway has to forward to each project's Railway-provided domain, never its public custom domain — because that custom domain now resolves to the gateway, so forwarding to it would loop straight back in. I also added a reconcile-on-boot step: if the gateway itself is redeployed mid-session, it checks what's actually running on Railway and resumes managing it, rather than leaving a stack up forever.
What I'd build next
A few things I'd add next: a small authenticated HTTP control API so pause and resume take effect on a running gateway instantly, without a restart; per-service toggles in the terminal UI; a periodic keep-alive for long-lived WebSocket demos, which today can be stopped mid-connection; and light rate-limiting on the public wake path, since anyone can trigger a boot — cost, not access, is the thing to guard there.
More than the code, this was an exercise in a few patterns: waking services on the first inbound request, stopping a whole stack as a unit rather than service by service, routing everything through a single always-on front door by hostname, and reconciling in-memory state after a redeploy. They'd transfer to any platform that exposes an API to start and stop services — Railway just happened to be where my demos live.