Render Tutorials
When deploys go wrong

Reading Render logs

⏱ 8 min

The method in step 02 lives or dies on log reading. Logs are how Render tells you what happened; everything else is inference. This step is the cheat sheet: where each log lives, how to navigate it without scrolling for ten minutes, and the CLI one-liners that beat the Render Dashboard for triage.

The three log surfaces

Render writes logs to three places, and a lot of confusion comes from looking in the wrong one:

flowchart LR
  events["Service<br/>Events feed"]
  deploy["Deploy logs<br/>(per deploy)"]
  runtime["Runtime logs<br/>(per service)"]

  events -->|"click a deploy"| deploy
  deploy -->|"build succeeded,<br/>now the service is live"| runtime
Log typeLives onContentsWhen you want it
Events feedService page → EventsTimeline of deploys, env var changes, scaling actions”What changed and when?”
Deploy logClick a deploy in EventsBuild output + pre-deploy output for that one deployBuild or pre-deploy failed
Runtime logService page → LogsStdout/stderr from the running serviceService is live but misbehaving, or boot is failing

The boundary between the deploy log and the runtime log is the moment the build succeeds and Render starts your startCommand. If your service crashes on boot, the crash is in the runtime log, not the deploy log - even though the deploy itself is marked as failed.

Anatomy of a deploy log

Every deploy log has the same shape, in order:

Deploy log structure
==> Cloning from https://github.com/your-org/your-repo
==> Checking out commit abc123 in branch main
==> Using Node version 20.11.0 (default)
==> Running build command 'npm ci && npm run build'
... build command output ...
==> Uploading build...
==> Uploaded build successfully
==> Running pre-deploy command 'npm run db:migrate'
... pre-deploy output ...
==> Pre-deploy command succeeded
==> Build successful 🎉
==> Deploying...
==> Running 'npm start'
... your application's stdout starts here ...

The ==> lines are Render’s own logging. They mark phase boundaries - those are your ruler. Everything between two ==> lines belongs to one phase, and the phase tells you which family of error you’re looking at.

A few patterns worth memorising:

  • ==> Build failed before any application output → buildCommand returned non-zero. See step 04.
  • ==> Pre-deploy command failed → preDeployCommand returned non-zero. Often a migration or a missing env var.
  • ==> Deploy timed out with no obvious crash → your startCommand is running but not binding the port, or your health check is failing. See step 05.
  • ==> Deploy failed after application output → your app crashed on boot. See step 06.

Finding the first error fast

The Render Dashboard’s deploy log viewer has a built-in search box. Use it. Don’t scroll.

  1. Open the failed deploy From the service’s Events feed, click the deploy row labelled Deploy failed (or Build failed).
  2. Search for `error` Case-insensitive search highlights every match. The Render Dashboard wires next/previous match like a normal text editor.
  3. Jump to the first match, not the last Errors at the top are causes. Errors at the bottom are usually the process exiting because of the cause.
  4. Read 5 lines above and 10 lines below Context above gives you “what was Render trying to do?”; context below gives you the stack trace.

The first error is almost never on the last screen. Train yourself to scroll up when you open a failed deploy, not down.

The CLI: faster for triage loops

Once you’ve diagnosed two or three failures from the Render Dashboard, the click cost adds up. The CLI is faster:

Terminal
# 1. Find the failing service's ID
render services -o json | jq -r '.[] | select(.service.name == "api") | .service.id'
# 2. List its recent deploys, with status
render deploys list srv-xxxxx -o json \
| jq -r '.[] | "\(.deploy.id)\t\(.deploy.status)\t\(.deploy.commit.id)"' \
| head -5
# 3. Stream the deploy's logs (build + runtime if applicable)
render logs -r srv-xxxxx --start 30m --limit 1000

For the most common loop - “show me the last error from the most recent failed deploy” - this one-liner is gold:

Terminal
SRV=srv-xxxxx
render logs -r "$SRV" --start 1h --level error --limit 50 \
| head -20

--level error filters server-side, so you don’t pull megabytes of info-level logs over the wire. head -20 is the “give me the first matches, not the last” knob.

The four signals you’re looking for

Most of triage is pattern recognition. Four log signals cover 90% of failures:

1. Stack traces

TypeError: Cannot read properties of undefined (reading 'split')
at parseConfig (/opt/render/project/src/config.js:42:18)
at Object.<anonymous> (/opt/render/project/src/app.js:8:14)
at Module._compile (node:internal/modules/cjs/loader:1256:14)
...

A stack trace is gold: it tells you the language, the file, the line, and the cascade. The top frame is the crash site; the bottom frames are how you got there. Open the file at the line number.

2. ==> lines from Render

==> Detected service running on port 10000
==> Detected service running on port 8080
==> Your service is live 🎉
==> No open ports detected, continuing to scan...
==> Timed out
==> Deploy failed

These tell you what Render saw. They’re the most reliable narrator of the boot process. Step 05 is mostly about decoding these.

3. Exit codes and signals

==> Application exited with code 1
==> SIGTERM received, shutting down gracefully
==> Application exited with code 0
==> Out of memory! Process was killed
==> Application exited with code 137

Exit code 0 = clean. Non-zero = error. Code 137 = OOM kill by the kernel. SIGTERM = Render asked the process to shut down (deploy, scale-down, or restart).

4. Render-specific warnings

==> Your service is using Node 18, but your package.json specifies "node": ">=20".
==> Set NODE_VERSION to override the default.

Render emits informational warnings about version mismatches, missing health checks, and other configuration smells. They show up in the deploy log between ==> lines. Don’t skim past them - they’re often the diagnosis already.

A working example

Here’s a real shape of a deploy log for a build that failed. Read it top-to-bottom and try to spot the first error before you read my annotation:

Deploy log: 'Build failed'
==> Cloning from https://github.com/example/app
==> Checking out commit 4f2a91c in branch main
==> Using Node version 18.20.0 (default)
==> Running build command 'npm ci && npm run build'
npm warn deprecated some-old-package@1.0.0
> app@1.0.0 build
> next build
SyntaxError: Unexpected token '??='
at internalCompileFunction (node:internal/vm:73:18)
at wrapSafe (node:internal/modules/cjs/loader:1153:20)
...
npm ERR! code 1
==> Build failed 😞

What the method tells us:

  1. Phase: build. The Build failed marker confirms it.
  2. First error: SyntaxError: Unexpected token '??=' on line 8.
  3. Render’s clue: line 3 - Node 18 is the default.
  4. Hypothesis: the ??= (logical-nullish-assignment) operator needs Node 16+. Wait, Node 18 has it. So what gives? Read the error again: it points to node:internal/vm, which means the project’s own build tooling (next build) is choking on the syntax. The fix is the same as a version mismatch - bump to Node 20 to match the project’s engines field, which is almost certainly set.

We dig into this exact failure in step 04. For now, the takeaway is that lines 3, 8, and the Build failed marker together gave you the diagnosis without ever scrolling.

Log retention and freshness

Two operational details that matter when you’re chasing an old failure:

  • Logs are eventually consistent. A line that just happened might take 1-2 seconds to appear. For live triage, render logs --tail is the most reliable view.
  • Retention depends on your plan. If --start 7d returns less than you expected, your plan’s retention window is shorter than that. The Render Dashboard’s log explorer will simply stop returning rows; the CLI does the same silently. For long-term forensics, ship logs to an external sink (see Render’s log streaming docs).
A deploy is marked 'Deploy failed' in the Events feed. You click into it and the build section is all green with `==> Build successful`. Where do you look next for the actual error?

What you learned

  • Three log surfaces: Events feed (timeline), deploy logs (build + pre-deploy), runtime logs (boot + live)
  • Deploy logs and runtime logs are different views. A 'Deploy failed' with a green build means look at runtime logs
  • `==>` lines are Render's own narration - they mark phase boundaries and emit warnings you should actually read
  • Search for `error` and jump to the FIRST match, not the last. Context is 5 lines above and 10 below
  • CLI shortcut for triage: `render logs -r $SRV --start 1h --level error --limit 50 | head -20`
  • Four signals cover 90% of failures: stack traces, `==>` lines, exit codes/signals, and Render warnings