Skip to main content
Self-hosting means you run the worker (your agent code) on your own infrastructure instead of Zero Runtime Cloud. The runtime still handles transport, GPUs, STT/LLM/TTS orchestration, and turn-taking, so there’s no media infrastructure for you to operate. When your process calls zeroruntime.serve(...), it opens an outbound connection to the runtime and registers your agent under the id in ZERORUNTIME_AGENT_ID. The runtime then dispatches sessions for that id to every registered worker and load-balances across them.
The CLI commands under Operate (up, version, secrets, logs, down) manage workers on Zero Runtime Cloud. They don’t apply here; use your own platform for rollouts, secrets, scaling, and logs.

Prerequisites

  • An agent project whose main.py calls zeroruntime.serve(...). Every quickstart example does.
  • Docker on the machine that builds the image (and on the host that runs it, for Docker).
  • For Kubernetes: a cluster, kubectl configured for it, and a container registry the cluster can pull from.
  • Your auth token from the Zero Runtime dashboard.

Choose an agent ID

On Zero Runtime Cloud, the platform assigns the agent ID for you. When you self-host, you choose it and set it as ZERORUNTIME_AGENT_ID. The worker registers under this ID, and everything that reaches your agent targets it: zeroruntime invoke --agent-id, the dispatch API, and telephony routing rules. Pick an ID that is:
  • Unique: don’t reuse it for a different agent. Workers that share an ID receive each other’s sessions.
  • Stable: a fixed name like support-agent, not a random value generated per run or per container. Every replica of the same agent uses the same ID; that’s how the runtime load-balances across them.
If ZERORUNTIME_AGENT_ID is unset, the worker registers as zeroruntime_agent. Sessions and calls aimed at your real ID then never reach it. Always set it explicitly.

Configure the environment

The worker reads its agent ID, connection details, and provider keys from environment variables. Create a .env file in your project folder. See Authentication for the full list.
.env
Keep comments on their own lines. docker run --env-file and kubectl --from-env-file don’t strip trailing # comments, so they’d become part of the value.
Pass these at runtime. Don’t bake them into the image: add .env to .dockerignore.
.dockerignore

Remove the local test invoke

For local testing, use the on_ready callback in serve() to start a playground session when the worker is ready. Remove it in production to avoid automatically creating a new room for every worker. In production, start sessions via zeroruntime invoke CLI, the dispatch API, or telephony routing rules.

Build the image

Use the Dockerfile that zeroruntime init generates, or write your own. It should install requirements.txt and run python main.py:
Dockerfile
Then build it from your project folder:

Run the worker

1

Start the container

Run the worker in the background, load your .env, and restart it if it exits:
The worker only dials out to the runtime. -p 8081:8081 is optional and only exposes the health endpoint.
2

Confirm it registered

Check the logs for a successful connection and registration under your agent ID:
Then check the health endpoint:
3

Add more workers (optional)

To handle more concurrent sessions, start more containers with the same .env. Use a unique container name and host port for each:
Both workers register under the same agent ID, and the runtime balances sessions across them.
4

Ship an update

Build a new tag, then replace the running container:
docker stop sends SIGTERM and waits 10 seconds by default. Raise it with docker stop -t <seconds> if your sessions need longer to wrap up.

Start a session

Self-hosted workers receive sessions the same way cloud workers do. Start one with the CLI, passing your ZERORUNTIME_AGENT_ID:
The CLI prints a playground link you can open to talk to your agent.

Connect telephony

To take or place phone calls, configure gateways and a routing rule that point at your agent. Use your ZERORUNTIME_AGENT_ID wherever an agent ID is required; it must match exactly, or calls never reach your workers.
1. Create an inbound gateway: the entry point for incoming calls.
  1. Copy the Inbound URL from the Zero Runtime dashboard.
  2. Go to Twilio and create a new SIP Trunk.
  3. Go to the Origination section, paste the Inbound URL there, and save it.
2. Create a routing rule: connects the inbound gateway to your self-hosted agent.
  1. From your inbound gateway, click Configure rule, then Create new routing rule.
  2. Enter a Routing Rule Name and select API Key authentication.
  3. Choose the Call Direction (Inbound).
  4. Enter the Phone Number and select a Room Type.
  5. Enter your ZERORUNTIME_AGENT_ID as the Agent ID and click Save.
3. Test it: with your workers running, dial your provider’s phone number from any phone. The agent greets you as soon as the call connects.
Using a different SIP provider? Follow its inbound setup: Telnyx, Vonage, or Plivo.
1. Create an outbound gateway: lets the agent dial out to the PSTN.
  1. In Twilio, open your SIP Trunk and go to the Termination section. Create a Termination URI and add username/password credentials.
  2. Back in the Zero Runtime Dashboard, open the outbound gateway configuration.
  3. Paste the Twilio Termination URI as the address, add the same username/password credentials, and save.
2. Place a call: with your workers running, trigger a call from the agent. Replace YOUR_ZERORUNTIME_AUTH_TOKEN and routingRuleId with your own.
For lower latency, run your workers in the same region as your SIP provider.
Using a different SIP provider? Follow its outbound setup: Telnyx, Vonage, or Plivo.

Operating self-hosted workers

  • Capacity: each worker accepts up to capacity concurrent sessions (default 10, set in zeroruntime.serve(...) or with ZERORUNTIME_MAX_CONCURRENT_SESSIONS). Add workers or replicas when you need more.
  • Health: each worker serves GET /health on port 8081 for liveness probes.
  • Rolling updates: build a new image tag and roll it out with your platform’s usual deploy process.
  • Credentials: rotate tokens and provider keys in your secret manager, then restart the workers so they pick up the new values.
  • Logs: workers log to stdout and stderr. Collect them with your existing logging stack.

Troubleshooting

  • Sessions or calls never arrive: the agentId in invoke, dispatch, or your routing rule doesn’t exactly match ZERORUNTIME_AGENT_ID, or the variable is unset and the worker registered as zeroruntime_agent. Check the worker logs for the ID it registered under.
  • Worker can’t connect: check ZERORUNTIME_TARGET and ZERORUNTIME_AUTH_TOKEN, and that outbound traffic to the runtime on port 443 is allowed.
  • Pods restart in a loop: check kubectl logs for a startup error, and raise the probe’s initialDelaySeconds if the worker takes longer to start.