AgentRunner is a long-lived process: it registers with the Unpod
orchestrator over WSS and heartbeats (at an interval the orchestrator assigns on
registration). Under the default dial_out transport it then dials out to a
per-call bridge each time a call is assigned; under the legacy serve transport
it instead hosts a bridge connection per call. Provisioning is covered
by the Provisioning checklist; this page is what
changes when you leave your laptop.
Since v2 the runner defaults to the
dial_out transport: both the control
connection and the per-call bridge are outbound from the runner, so a runner
behind NAT needs no public reachability, serving_url, or agent_secret. The
reachability and authentication steps below apply only when you opt into the
legacy transport="serve" model. See
AgentRunner constructor.What production needs
A production deployment should have:- A stable
UNPOD_API_KEY- required by theAgentRunnerfor the orchestrator connection (full variable table) - A clear
agent_idshared by every replica of the same agent - Capacity limits (
max_sessions,permits_per_minute) that match your traffic - Shutdown handling so active calls can drain before the process exits
- Observability for active calls, failures, and latency
- (legacy
servetransport only) a publicserving_urlso Unpod can reach the runner bridge, and anagent_secretso inbound bridge connections are signed and verified
- Set the environment variables.
- Run multiple replicas with the same
agent_idwhen you need scale. - Restart the process under a supervisor.
- (legacy
servetransport only) expose the runner publicly and lock down the bridge withUNPOD_AGENT_SECRET.
Production checklist
1. Reachability - serving_url (legacy serve transport only)
This step applies only to
transport="serve". Under the default dial_out
transport the runner never listens - it dials out to the bridge URL delivered in
each job.assign frame - so no public reachability is needed.serve mode the runner hosts a per-call bridge that Unpod dials into. It
binds to 0.0.0.0:8765 by default; in production, tell the orchestrator where
that bridge is publicly reachable:
serving_url=... to AgentRunner). The host/port must be reachable
from Unpod.
2. Authentication - agent_secret (legacy serve transport only)
Also
serve-only. Under the default dial_out transport the per-call bridge is
authenticated by the per-call token embedded in the job.assign bridge_url, so
agent_secret is ignored (the constructor warns if you pass it).serve mode, with an agent secret set, every inbound bridge connection is
HMAC-verified (signed URLs, replay-protected). Without one, the runner accepts
unsigned connections - acceptable only for local dev.
3. Capacity
4. Scaling - add replicas
Run more processes with the sameagent_id - on one machine or many. They
form a pool; the orchestrator load-balances dispatches across replicas by
reported capacity. No shared state, no coordination needed.
5. Shutdown and supervision
OnSIGTERM the runner stops accepting dispatches and drains active calls for
up to drain_timeout_s before exiting - container- and systemd-friendly.
Under the default
dial_out transport the runner auto-reconnects its
orchestrator control socket with jittered exponential backoff (1s → 30s cap) and
re-registers under the same worker_id; in-flight calls ride their own bridge
sockets and survive a control-socket drop. Still run it under a supervisor with
restart-on-exit (systemd Restart=always, Kubernetes restart policy, Docker
--restart) to recover from process crashes and from a non-retriable transport
rejection (an orchestrator too old to acknowledge dial_out). The legacy
transport="serve" model does not auto-reconnect.Environment summary
The minimum production.env - every variable defined in the
Provisioning checklist:
AgentRunner(base_url=...)overridesUNPOD_ORCHESTRATOR_URLAgentRunner(serving_url=...)overridesUNPOD_RUNNER_URLAsyncClient(base_url=...)overridesUNPOD_SERVICE_BASE_URL