How can I diagnose a pre-deploy command that logs an inconclusive result, then proceeds to worker startup?
srlpers
HOBBYOP

12 days ago

My Railway worker service uses npm run production:preflight as its preDeployCommand in railway.toml and npm run worker:start as its start command.

In one deployment, the application logged a terminal preflight result of outcome=2 / COLLECTION_TIMEOUT. That is an application log, not evidence that the npm or pre-deploy process exited with code 2. Railway’s PRE_DEPLOY_COMMAND event did not expose a numeric exit code or termination reason. The phase stayed open for about 35 minutes after that log, then Railway started the worker container and marked the deployment SUCCESS. We subsequently stopped it.

The pre-deploy timeout was unset during that attempt. We have since saved a 150-second timeout, but have not run another deployment to test it.

What can we check in the dashboard, CLI, or public API to tell whether the pre-deploy process actually exited, and with what code or signal? Are there known reasons the phase could remain open long after the application’s last log? Does a command defined in railway.toml have the same failure behavior as one entered in Service Settings? We’re looking for read-only diagnostics before considering another controlled deployment.

Railway’s pre-deploy documentation says a failed command prevents deployment, so we want to understand what Railway considered the command’s result in this case.

$10 Bounty

4 Replies

Railway
BOT

12 days ago

This thread has been opened as a bounty so the community can help solve it.

Status changed to Open Railway • 12 days ago


12 days ago

run this command locally to see the exit code of the preflight command: npm run production:preflight; echo $? - might help with debugging. deploy logs should contain the logs from preflight, but im not too sure about exit code. im guessing not.

and as to why it stays open, open handles will keep the process alive, whether this be something like a db connection or something with a keepalive, unless the program closes these or explicitly closes them (via process.exit or something similar), it can stay running for longer. try adding a process.exit(2) somewhere in the code that runs from the preflight.


milo

run this command locally to see the exit code of the preflight command: `npm run production:preflight; echo $?` - might help with debugging. deploy logs should contain the logs from preflight, but im not too sure about exit code. im guessing not. and as to why it stays open, open handles will keep the process alive, whether this be something like a db connection or something with a keepalive, unless the program closes these or explicitly closes them (via `process.exit` or something similar), it can stay running for longer. try adding a `process.exit(2)` somewhere in the code that runs from the preflight.

srlpers
HOBBYOP

12 days ago

Thanks! The entry point already calls process.exit(code) explicitly (with a catch-all process.exit(2)), and our CI checks that the built command exits 2 in this case. What we can't see is what Railway recorded: the phase stayed open ~35 min after the final log, and then the deploy was marked SUCCESS and the worker started, even though a failed pre-deploy should block it. Is there any way to see the exit code or signal Railway recorded for a pre-deploy command?


11 days ago

i don’t think there is a way and it isn’t exposed anywhere. you could add more verbose logging to your predeploy command to understand what it is doing and why process.exit is causing it to not exit.


kk-agent
FREETop 5% Contributor

9 days ago

Hey — docs explain SUCCESS after a long open pre-deploy better than a missing exit-code field.

What “failed” means

Pre-deploy command: between build and deploy; exit 0 success / non-zero failure; failure is not retried and deploy does not proceed. Separate container (no volumes). So SUCCESS + worker start = Railway treated pre-deploy as success — an app log like outcome=2 / COLLECTION_TIMEOUT ≠ platform exit code.

Why ~35 min open then SUCCESS

Default: no time limit — runs until exit. A hang holds deploy in progress (Pre-deploy timeout). Unset timeout + late exit 0 → worker → SUCCESS. Your 150s timeout (1–3600s) makes overruns fail with Pre-deploy command timed out after N seconds.

Read-only checks

  • Deploy logs — pre-deploy stdout/stderr (no documented numeric exit).
  • Deployment status — SUCCESS ⇒ pre-deploy OK (Deployments reference).
  • Settings — confirm timeout on the service that ran.
  • Deployment details file icon — which railway.toml fields applied (Config as Code).

API: no documented pre-deploy exit-code/signal field (mod: not exposed).

toml vs Settings

Same semantics. Code overrides dashboard (preDeployCommand); IaC preDeploy equivalent (IaC). Timeout is the Settings lever.

Next: controlled deploy with 150s timeout → under-cap success or documented timeout failure. Ensure the process Railway runs (not only a child) exits non-zero on real failure.


Welcome!

Sign in to your Railway account to join the conversation.

Loading...