← Blog

Why a deploy actually fails, reason by reason

Railyard

September 30, 2026

“The deploy failed” isn’t one problem — it’s at least a dozen different ones that happen to show up the same way to someone skimming a dashboard. Knowing which one actually happened is most of the fix, which is why a failed deploy’s page leads with why it failed: the cause, the fix, and the log line that proves it.

The two that look identical but aren’t

The single most common confusion: “the build ran out of time” (stopped at a 60-minute limit; the next attempt reuses what already built) versus “the build ran out of memory” (a build step killed outright for exceeding what the build server has). Both look like “the build just didn’t finish” from a glance at a red status — the fix for one is patience and reusing partial work, the fix for the other is a bigger build server or deploying a pre-built image instead of building on the platform at all.

The app runs, but somehow doesn’t

This one deserves its own callout because it’s rarely obvious from the app’s own code: the container is running, but nothing answers HTTP. Not crashed — running, healthy by every metric except the one that matters. The actual cause is almost always a one-line fix: the app has to listen on 0.0.0.0, not 127.0.0.1, and on the port in $PORT, not a hardcoded one. A perfectly correct app, running perfectly correctly, that only ever talks to itself.

A close relative: the command ran in the wrong folder — the image doesn’t contain the app where the start command expects it, usually because a Dockerfile written for local development (expecting a bind mount) got used for a real build. Switching to Railpack instead of the repo’s own Dockerfile is the fix when that’s the actual situation.

The database-shaped failures, three different ways

  • The database wasn’t reachable — most confusing when it happens during the build, because an app connecting to its database while compiling assets or running a build-time check is trying to reach infrastructure that build step was never going to have access to.
  • The database user lacks a permission — a migration needs something the app’s database user doesn’t have yet. The fix is almost suspiciously simple: redeploy once, since grants refresh on every deploy.
  • The release step failed — usually migrations, run once before the new version takes traffic. The actual error’s first line shows directly on the deploy page, not buried in a full log dump.

The two that are really the same problem wearing different clothes

A required variable isn’t set (the app stops at boot because something it needs is empty) and the app needs credentials for an outside service (a third-party API, cloud storage, email, payments — no key configured) are the same root cause — a missing environment variable — with different specificity. One is “something’s missing,” the other names which category of something.

The image couldn’t be pulled (doesn’t exist, wrong tag, or private without credentials) and the image name is invalid (characters Docker won’t accept) are the equivalent pair for image-based deploys instead of build-based ones.

Why naming the exact cause matters more than it sounds

Every one of these produces the same visible symptom to someone glancing at a dashboard: a red status and a failed deploy. The actual fix only becomes obvious once the specific cause is named — “ran out of time” and “ran out of memory” need opposite responses; a dependable deploy pipeline is less about never failing and more about a failure that tells you immediately and specifically what to do next.

Full reference, every cause: Troubleshooting failed deploys.

See the exact cause on the deploy page.

Every failed deploy shows the cause, the fix, and the log line that proves it — not just a red X.

Free plan, no card, and 14 days of Pro for every new team. One flat price per team.