Render Tutorials
Pair-program with Render: deploy, debug, and monitor with the Render plugin

Debug with the agent

⏱ 10 min

The deploy is red. The agent is staring at a fresh prompt: “The latest deploy of tinylink-web is failing. Can you take a look?” From here you do almost nothing - the plugin does the work.

1. Watch the diagnosis unfold

A typical session walks through four phases. Yours will vary in wording, not in structure.

Phase 1: orient

Agent (transcript, abridged)
Loading skill: render-debug.
Step 1/4 - figure out what's failing.
Calling render -> list_services()...
-> found tinylink-web (web_service, oregon)
Calling render -> list_deploys(serviceId="web-srv-ghi789", limit=3)...
-> dep_001 status=live 5h ago
-> dep_002 status=build_failed 2m ago commit=4f8a2b

The agent uses MCP to ground itself in real state - not what it thinks your services are, but what they actually are right now.

Phase 2: read the failure

Agent (transcript, abridged)
Step 2/4 - inspect the failed deploy.
Calling render -> get_deploy(deployId="dep_002")...
-> status=build_failed
-> finishedAt=2026-05-21T20:34:11Z
-> commit.message="feat: tighten uvicorn binding (do not merge)"
Calling render -> get_deploy_logs(deployId="dep_002", tail=200)...
-> INFO: Uvicorn running on http://127.0.0.1:10000
-> INFO: Application startup complete.
-> ==> No open ports detected, continuing to scan...
-> ==> Detected service running on port 10000 - unreachable (only listening on 127.0.0.1)
-> ==> Timed out
-> ==> Exited with status 1

This is where MCP earns its keep - the agent isn’t speculating about why the deploy failed, it’s reading the actual log lines from the failed run.

Phase 3: diagnose

Agent (transcript, abridged)
Step 3/4 - root cause.
The skill render-debug pattern-matches this on the canonical Render
port-binding failure. Two pieces of evidence:
- 'Uvicorn running on http://127.0.0.1:10000'
- 'No open ports detected ... only listening on 127.0.0.1'
The container starts, the app starts, but the proxy can't reach it
because uvicorn is bound to the loopback interface only. Render's
load balancer connects from outside the container.
Checking the commit diff to confirm:
Calling render -> get_commit_diff(commit="4f8a2b")...
-> startCommand: uvicorn main:app --host 0.0.0.0 --port $PORT
-> +startCommand: uvicorn main:app --port $PORT
Confirmed. The fix is to restore `--host 0.0.0.0`.

The skill bundles this canonical pattern - along with about a dozen others (missing env var, OOM, dependency install failure, healthcheck wrong path, etc.) - so the agent doesn’t have to reinvent it from logs alone.

Phase 4: fix and ship

Agent (transcript, abridged)
Step 4/4 - apply the fix.
Editing render.yaml...
startCommand: uvicorn main:app --host 0.0.0.0 --port $PORT
Hook: render.yaml validated, no errors.
Committing and pushing...
git add render.yaml
git commit -m "fix: bind uvicorn to 0.0.0.0 so Render's proxy can reach it"
git push
Calling render -> watch_deploy(serviceId="web-srv-ghi789")...
-> build_in_progress ...
-> update_in_progress ...
-> live /healthz returning 200
Fixed. Root cause: the previous commit removed --host 0.0.0.0 from the
uvicorn start command, leaving the app bound to the loopback interface.
Restored it. Deploy is green.

That’s the whole thing. Four MCP tool calls, one skill, one one-line diff. Total wall-clock, not counting build time: about a minute.

2. The canonical port-binding rule

This failure mode is so common it’s worth memorizing. Reveal the rule when you’ve talked the agent through the fix:

Show hint

A web service on Render must bind to 0.0.0.0 (all interfaces) on the port given by the $PORT environment variable.

  • Binding to 127.0.0.1 works locally but fails on Render with “No open ports detected.”
  • Binding to a hardcoded port like 8000 may also fail - Render assigns $PORT dynamically; in production it’s typically 10000, but never bet on a specific value.
  • Frameworks vary. The flag is --host for uvicorn/hypercorn, -b 0.0.0.0:$PORT for gunicorn, app.listen(process.env.PORT, "0.0.0.0") for Express.

The plugin’s render-platform.mdc rule encodes this so the agent doesn’t drift on it between sessions.

3. Confirm the fix from outside

Once the agent says “live,” verify yourself instead of taking its word for it:

Terminal
$curl https://tinylink-web.onrender.com/healthz
{"status":"ok"}
$curl -X POST https://tinylink-web.onrender.com/links -H 'Content-Type: application/json' -d '{"url":"https://github.com/render-oss/skills"}'
{"slug":"a9X2Zk"}
$curl -I https://tinylink-web.onrender.com/a9X2Zk
HTTP/2 307
location: https://github.com/render-oss/skills

The 307 means the redirect path is healthy end-to-end - web service -> Postgres -> response.

4. Why this is more than a one-trick pony

The pattern you just watched generalizes. render-debug knows about:

  • Missing or misnamed env vars (the agent reads them from get_service and compares against what your code references)
  • Out-of-memory kills (it reads metrics over MCP and correlates with the deploy timeline)
  • Healthcheck path mismatches (compares healthCheckPath in your Blueprint to the routes in your code)
  • Dependency-install failures (parses build logs for known package-manager error signatures)
  • Database connection issues (cross-checks DATABASE_URL plumbing against the Blueprint and the code)

For each, the playbook is the same: read live state via MCP, pattern-match on logs and metrics, propose a fix, apply it, watch the redeploy.

The agent's debug pass made four MCP tool calls before proposing a fix: list_services, list_deploys, get_deploy, get_deploy_logs. Why those four, in that order?

What you learned

  • Asked the agent a vague question ("the deploy is failing") - it grounded itself in real state with MCP
  • Watched render-debug walk through orient -> read logs -> diagnose -> fix in four MCP tool calls
  • Saw the agent pattern-match the 'no open ports detected' failure against the canonical Render port-binding rule
  • Restored the fix in one diff, redeployed automatically, and verified from outside