← All posts

Build Logs vs Runtime Logs: A Practical Guide to Debugging a Failed Deploy

October 31, 2026

TL;DR: A deploy fails in one of two places: while your code is being turned into a runnable image (read the build log, from the bottom up) or after your app starts (read the runtime log, from the first error down). Figure out which side of that line you’re on before you change anything, and most deploy bugs take minutes instead of an evening.

When a deploy breaks, the instinct is to start changing things: bump a version, tweak a config file, push again and hope. That loop is slow because each push costs a full build. The faster path is to read the right log first. This guide explains what each log contains, how to tell which one you need, and the failures that show up again and again.

What’s the difference between a build log and a runtime log?

They cover two different stretches of time.

  • The build log covers everything between “a commit arrived” and “an image exists”: cloning the repository, detecting the stack, installing dependencies, compiling assets. Your app’s code hasn’t run yet. If something fails here, nothing new is serving traffic.
  • The runtime log covers everything your app prints after the process starts: the framework’s startup banner, request lines, warnings, stack traces. If something fails here, the build was fine and the problem is in how the app boots or behaves.

On Candor Deploy the two live in different places in the portal. Build output sits on the app’s Deployments tab, where each deployment in the history expands to show what its build did, and a copy is saved when the build finishes so the output of older builds stays there whether they succeeded or failed. Runtime logs sit on the Logs tab and refresh every few seconds with the most recent lines (troubleshooting docs).

How do you tell which log you need?

Look at where the deploy stopped. Most hosts show a phase or status for each deployment, and that phase tells you which log to open.

On Candor Deploy, each deployment shows one of six phases: Queued, Building, Deploying, Live, Failed or Canceled (troubleshooting docs). A rough map:

  • Failed means the build didn’t complete. Open the build output.
  • Stuck in Deploying means the build worked but the new version never proved it was up. That’s almost always a runtime problem, so open the runtime logs.
  • Live, but the app misbehaves means a runtime problem in your code or configuration. Runtime logs again.

That one decision, build or runtime, rules out half the possible causes before you’ve read a single line.

How should you read a build log?

From the bottom. Build tools print a lot of progress noise, and the actual reason for a failure is almost always in the last few lines. Scrolling from the top means reading hundreds of lines of successful dependency installs before you reach the one line that matters.

Candor Deploy leans into this: build output keeps the last 2,000 lines, and a longer build is trimmed from the top with a visible marker, because the failure is at the end (troubleshooting docs).

What you’ll typically find at the bottom:

  1. A dependency or compile error. The same error you’d hit building the project on your own machine: a missing package, a type error, a lockfile out of sync with the manifest. Reproduce it locally with a clean install, fix it, commit and push.
  2. The stack wasn’t recognized. Auto-detecting builders look for marker files such as package.json, pyproject.toml or go.mod. If those aren’t in the directory being built, detection has nothing to go on. In a monorepo, check which subdirectory the host builds; on Candor Deploy that’s the Root directory you set when creating the app, and leaving it blank builds the repository root (troubleshooting docs).
  3. The build hit a resource limit. Builds usually run with fixed CPU, memory, disk and time budgets. A build that ends abruptly, especially near its time limit or while downloading something huge, has probably hit one. Candor Deploy publishes its build limits in the same docs page, so check them there rather than guessing.

A useful habit: when the build log shows an error you don’t recognize, copy the exact line and run the same build command locally in a clean checkout. If it fails the same way, it’s your project, not the platform.

How should you read a runtime log?

Differently. Runtime logs are a stream, not a report, so the useful line is usually the first error after startup, not the last line on screen. Restart loops make this worse: the same crash repeats, and the newest lines are just the latest copy of it.

Read for these, in order:

  1. Did the app print its startup banner? If your framework normally says something like “listening on port…” and you don’t see it, the app crashed before it got there. Look just above where the banner should be.
  2. Which port and interface did it bind? An app listening on localhost:3000 inside a container can’t be reached by the platform’s router. If the log shows a hard-coded port or 127.0.0.1, that’s your bug.
  3. What’s the first stack trace? Missing environment variables and database connection errors usually throw on boot, with the variable or host name right in the message.

Structured logging helps a lot here. If your app writes JSON lines with a level field, a log viewer can filter to errors and skip the noise. Candor Deploy’s Logs tab parses lines into rows with timestamps and levels, filters by level, supports search and live-tail, and unwraps structured JSON so you see the message itself (changelog: readable logs).

Why does a deploy hang in “Deploying” and never go live?

This is the classic runtime failure that looks like a platform problem. The build succeeded, the container started, and then nothing.

The usual cause is the port. Hosts tell your app which port to use through a PORT environment variable, send traffic there, and run a health check against it. On Candor Deploy, the platform sets PORT inside the container (default 8080), routes traffic to it and health-checks it by connecting to it. An app that hard-codes another port fails all three, so the deployment stays in Deploying and the previous version keeps serving in the meantime (troubleshooting docs).

The fix is two lines of config: read the port from PORT, and listen on 0.0.0.0 rather than localhost. Our earlier post on the $PORT and 0.0.0.0 fix walks through it framework by framework.

What if the runtime log is empty?

An empty runtime log is information too. Two common meanings:

  • The app never had a successful deploy to run, so there’s nothing to print yet. Go back to the build output.
  • The process was killed before it could print anything. Repeated restarts with no output often point to memory: if the app exceeds its memory limit, it’s stopped and restarted, frequently without a helpful message. On Candor Deploy, the Usage tab shows live memory against the limit, and a reading that sits near 100% is your answer (troubleshooting docs).

What are the most common runtime crashes?

After the port, three causes cover most crash-on-boot logs:

  • A missing environment variable. The stack trace usually names it. Add it in your host’s settings, and remember that most platforms apply new values only on the next deploy. On Candor Deploy you add variables on the Environment tab, one KEY=value per line, and saved values apply on the next deploy (troubleshooting docs). We covered the build-time versus runtime distinction in environment variables explained.
  • A database connection error. Check that the connection string is set, that the database finished provisioning before the app booted, and that the URI scheme matches your driver. For example, SQLAlchemy with psycopg 3 expects postgresql+psycopg:// rather than postgresql://.
  • A failing migration on startup. If your start command runs migrations, a bad migration crashes boot. See running migrations on deploy safely for a pattern that keeps old and new versions compatible.

What should you include when you ask for help?

If the logs don’t explain it, a good support request saves a round trip. Include the app’s name and URL, the short commit SHA of the deployment, the phase it’s stuck in, the last few lines of the relevant log, and what changed most recently, such as a new dependency, variable or domain (troubleshooting docs). That list is worth following on any platform, and for any teammate you’re asking.

The short version

Find the phase, pick the log, and read it the right way: build output from the bottom, runtime logs from the first error. When you’re ready to try it on your own app, start with the Candor Deploy quickstart.