Guide

When a deploy fails: find the cause and roll back

Tell a build, pre-deploy or readiness failure apart, read the logs that explain it, and roll back to an earlier release through the API when needed.

A deploy on JetDeploy goes through three steps: build, pre-deploy script, rollout. It can fail in any of them, and which one failed decides two things: whether your users noticed anything, and where to look for the cause. This guide covers both, and how to put the previous version back when you need to.

Deployoperation FAILUREBuildfailed or > 1200 sPrevious version keeps servingPre-deploy scriptfailed or > 510 sPrevious version keeps servingNot ready in timeexits · OOM · no portRollout goes onnew version takes over when ready
Only a readiness failure lets the new version go live; the other two leave the previous one serving.

Step 1: which step failed

The operation ends in FAILURE, and its error_message already says a lot. Read it on the App page, or through the API:

bash
curl -sS -H "Authorization: Bearer $JD_TOKEN" \
  https://jetdeploy.com/api/v1/operations/871 | jq '{status, error_message, commit}'
Failed step
Build, or build over 1200 s
The previous version
keeps serving
error_message carries
the failing build phase and the last lines of the build log
Failed step
Pre-deploy script, or over 510 s
The previous version
keeps serving
error_message carries
the exit code of the last attempt and the last lines of its output
Failed step
New version not ready in time
The previous version
the rollout goes on; the new version takes over once ready
error_message carries
per process: it exits, it runs out of memory, or nothing listens on its port

Step 2: read the right logs

Build and pre-deploy: the deploy logs

The deploy logs of a run hold three streams: build, predeploy and outcome. Read a page of them:

bash
curl -sS -H "Authorization: Bearer $JD_TOKEN" \
  "https://jetdeploy.com/api/v1/apps/42/deploy-logs?operation=871" | jq -r '.rows[] | "\(.stream)\t\(.message)"'

What to look for:

Build: the first error, not the last line. With npm, pip or go build, the real cause is usually a few lines above the final summary.

Pre-deploy: each attempt starts with a Pre-deploy attempt N line. A traceback there is from your migration or script, run with the App's environment variables.

Readiness: the logs

When the new version does not become ready, the cause is in the logs of the process named in the error. Filter by level to skip the noise:

bash
curl -sS -H "Authorization: Bearer $JD_TOKEN" \
  "https://jetdeploy.com/api/v1/apps/42/logs?pod=7&level=error&level=warn"

pod takes the id of the process, as listed in GET /api/v1/apps/42. Repeat level for several levels; a comma-separated value is refused.

The three causes the error message names, and what usually produces them:

  • The process exits. A missing environment variable read at startup, a failed connection to a database that is not ready, or a typo in the process command.
  • It runs out of memory. The process is killed for using more memory than it may, often at startup from too many worker processes: gunicorn --workers, puma workers, Node cluster mode.
  • Nothing listens on its port. The server binds 127.0.0.1 instead of 0.0.0.0, or a port other than the container port. JetDeploy sets no PORT variable, so a server that reads PORT falls back to its own default unless you set it.

Step 3: fix forward or roll back

Most failures in the build or the pre-deploy script are fixed forward: correct the code, push again. The previous version is still serving, so there is no hurry.

When a new version went live and misbehaves, roll back. Through the API, list the releases:

  1. bash
    curl -sS -H "Authorization: Bearer $JD_TOKEN" \
      https://jetdeploy.com/api/v1/apps/42/releases | jq '.[] | {id, commit, deployed_at, current, available}'
  2. Then roll back to one that is available:

    bash
    curl -sS -X POST -H "Authorization: Bearer $JD_TOKEN" -H 'Content-Type: application/json' \
      -d '{"release": 318}' https://jetdeploy.com/api/v1/apps/42/rollback

It answers 202 with an apply operation, which you follow like any other. From an AI agent, the same calls are the list_releases and rollback_app tools.

What a rollback does and does not do:

It puts the image of that release live with the configuration stored now: environment variables, processes, domains and redirect rules as they are saved.

It does not run the pre-deploy script, and does not reverse database migrations. The earlier code has to work with the current schema.

The registry keeps the image the App runs plus the last three builds. Older releases show available: false and cannot be rolled back to.

The next push deploys the new commit as usual.

A short runbook

  1. Open the failed operation and read error_message.
  2. Build or pre-deploy failed? The old version is serving. Read the deploy logs, fix, push.
  3. Readiness failed? Read the logs of the named process, filtered to errors.
  4. Is the new version live and broken? Roll back to the last good release, then fix forward.
  5. Did the failed deploy include a migration? Check that the previous version works with it before rolling back.

Next step

Most readiness failures come from the process itself: its bind address, its port, its memory at startup. Graceful shutdown in Go and Next.js standalone builds show a correct setup for two common stacks, and Database migrations in the pre-deploy script covers schema changes a rollback can live with.

Deploy it on JetDeploy

Push your code with git: JetDeploy builds it from your Dockerfile and serves it over HTTPS with zero-downtime deploys, next to managed databases.

Related Articles

All posts