Render Tutorials
Deploy an AI agent three ways on Render

Scale and see the cost

⏱ 5 min

3. Scale out

Submit several reviews quickly against the queue-agents-web URL. Use the LlamaIndex baseline PR, the OpenAI Agents trace PR, or any other public PR from the dashboard picker. With one worker, jobs queue up and drain one at a time. Use the dashboard’s Status and Run time (s) columns as your quick read on how long each run waits and works.

Now add workers:

  1. Open the worker In the Render Dashboard, open queue-agents-worker.
  2. Raise the instance count Go to Scaling and set instances to 3. This is the numInstances field from the Blueprint.
  3. Resubmit several reviews Submit several reviews again and compare the new rows’ status changes and run times.

Jobs move through the Key Value stream and spread across the worker instances. Throughput went up with no code change. The agent did not learn to be faster; you gave it more places to run.

flowchart LR
  web["queue-agents-web<br/>producer"]
  kv[("Key Value<br/>stream + consumer group")]
  w1["worker 1"]
  w2["worker 2"]
  w3["worker 3"]

  web -->|enqueue| kv
  kv --> w1
  kv --> w2
  kv --> w3

4. Survive a restart

The other payoff is durability. The work no longer lives in a request, so killing the request can’t kill the work.

  1. Start a review Submit a public PR and confirm a worker picks it up in the logs.
  2. Restart the web service While the review is in flight, redeploy or restart queue-agents-web from the Dashboard.
  3. Check the result The worker finishes independently. When the web service comes back, the completed review row is already in Postgres and visible in the dashboard.

In Pattern 1, that restart would have lost the run. Here the job was already on the queue and the worker was already running it, both outside the web service’s lifecycle.

5. Count what you now own

Scale and durability were not free. Open the queue helper again and count the coordination layer:

Use packages/queue-agents/src/kv.ts.

Use packages/queue_agents/src/queue_agents/kv.py.

  • The stream and the consumer group.
  • Blocking reads that wait for the next job.
  • Acks, so a job isn’t lost or double-run.
  • Retry-on-failure for un-acked messages.
  • The pub/sub channel that reports progress while the dashboard reads durable review state.

It is not a huge file. But every line is coordination code you now own, debug, and keep correct. There is still no built-in trace of which agent ran where or how long each step took.

Troubleshooting

Find the symptom that matches what you’re seeing, then apply the fix.

Scaling to 3 workers shows no throughput change. With the mock model each review finishes in milliseconds, so one worker never visibly bottlenecks. Submit a burst (5+ reviews at once) so the queue backs up, wait for all three instances to go live, then watch the Status and Run time (s) columns. The contrast is clearest with a real model, where each review takes seconds.


The restart-durability demo finishes before you can restart. A mock review is near-instant, so use a real model or a larger PR. Confirm a worker picked up the job in its logs first, then restart queue-agents-web (the web service, not the worker). The completed row lands in Postgres independent of the web lifecycle.


A failing job retries on a ~30s delay, not instantly. On failure the entry stays pending and is redelivered only after a 30-second idle reclaim window. The focused test forces immediate retry, so it looks instant there but waits 30s in the deployed worker. A worker log looping on the same entry id every ~30s is a failing handler being reclaimed, not a hang.

What you learned

  • Scaled the worker to multiple instances and watched throughput rise across several reviews
  • Restarted the web service mid-review and confirmed the work survived in Postgres
  • Counted the coordination code you now own: stream, consumer group, acks, retries, pub/sub
  • Set up the contrast for Pattern 3, where Render Workflows replaces that coordination