Hosts and agents
Runaway splits into two roles. The hub is the orchestrator and web UI — it makes every decision about what should run where. An agent runs on each Docker host and does the grunt work: spawning, reaping, and inspecting runner containers by id, and streaming events back. The hub thinks; the agent does.
Agents dial out
Section titled “Agents dial out”Each agent opens a connection to the hub over an authenticated WebSocket. The hub never reaches into a host. That direction matters:
- No inbound port on the host. The agent makes the outbound connection, so your Docker machines can sit behind NAT with nothing exposed.
- No SSH, no Tailscale, no
tcp://Docker. There is no credentialed login to your hosts, no dependency on a third-party mesh VPN, and no permanently open Docker endpoint for anyone to find.
This is what makes a hub on a public server driving agents on homelab boxes a natural shape rather than a networking project.
Trust is one-way
Section titled “Trust is one-way”The hub trusts no agent code, and the agent trusts no hub code. The agent exposes only the handful of operations the hub needs and nothing else — it does no scheduling, keeps no job model, and never decides anything on its own. The hub can ask an agent to pull a newer published image by naming a version, but it can never push code onto a host.
The hub holds no Docker socket
Section titled “The hub holds no Docker socket”Every host runs an agent — including the hub’s own machine, where the agent ships as a second service in the same Compose stack and enrolls itself on first boot. The hub itself has no Docker socket at all.
To run runners only on remote machines, don’t bundle a local agent alongside the hub. A socket-less hub schedules onto other hosts only.