Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Deployments, Health Checks & Rollback

Every push, promotion, and rollback is a deployment — a numbered record you can follow from the moment it starts to the moment it’s serving traffic, or fails with a reason you can act on. This page covers the stages a deployment goes through, how to follow one live, health checks, the release command, and rolling back.

What happens after git push

StageWhat’s happening
ReceivedYour push — or a promote or rollback you triggered — was accepted and given a deployment number.
QueuedWaiting for a build slot.
Buildingpreparing → detecting your app → restoring cache → building → exporting. The build log streams live.
BuiltThe new version is ready. With Automatic deployment on, it moves straight into release; with Automatic deployment off it waits here as waiting to be deployed until you deploy it.
Running release commandIf the app has a release command, it runs once against the new version before anything else starts.
ReleasingThe new version is being rolled out.
StartingNew instances are starting and being health-checked. Old instances keep serving traffic until the new ones pass.
LiveThe new version is serving traffic.

A deployment that doesn’t reach Live ends in one of these instead:

OutcomeMeaning
FailedThe build or release command failed (the previous version keeps running), or there was no earlier version to go back to.
Rolled backThe new version wasn’t healthy, and the previous version was restored.
Rollback failedThe new version wasn’t healthy, and the previous version wasn’t healthy either — or automatic rollback is off for this app. The platform takes no further automatic action — see When a new version isn’t healthy.
ReplacedA newer push or deployment superseded this one before it finished.

A build that doesn’t start within 30 minutes of the push, stops reporting for 5 minutes, or runs longer than 3 hours is marked Failed with the reason; push again. (A 3-hour build that does eventually finish is still deployed.) A promotion or rollback that isn’t applied within 10 minutes is marked Failed too.

See Health checks and Release command for what can make a deployment fail.

Following a deployment

A push prints its deployment number, a link to watch it live, and the paas watch command to run:

remote: Deploy #42 started for abc1234 → web, worker
remote:   Follow live: https://d2o.io/orgs/acme/projects/shop/envs/production/deploys/42
remote:   Or run:      paas watch --deploy 42

The push returns as soon as the deployment is created — it doesn’t wait for the build.

In the dashboard, the printed link opens the deployment page: a stepper for the current stage, one row per app with its health-check progress (or the reason it’s waiting), live build and release logs, and — if it fails — the failure reason and a Roll back button. While a deployment is in flight, the app and environment pages, and the project’s pipeline board, show a live “Deployment #42 · Building” chip that links to the same page. The app’s Deployments tab lists that app’s deployments (a shared build also names its other apps); the Environment’s Deployments tab lists every app’s.

From the CLI, paas watch follows a deployment stage by stage and streams its build and release logs until it finishes:

paas watch --deploy 42

Without --deploy, it follows the newest deployment for the app or Environment inferred from your paas git remote. Useful flags:

  • --quiet — print stage changes only, without the log output.
  • --timeout <SECONDS> — how long to wait before giving up (default 1800).

paas watch only exits once the deployment is final — including after an automatic rollback finishes — and retries on a network blip rather than giving up. It exits 0 when the deployment reaches Live, 1 when it ends Failed, Rolled back, Rollback failed, or Replaced (a newer deployment took over), and 2 on a timeout or a usage/connection problem — so it’s a reasonable gate in a CI pipeline:

git push paas main && paas watch

Like paas logs --tail, it streams, so it rejects --json. Watching requires an account API token; a Git deployment token cannot authenticate it. See CI deployment verification.

Health checks

With a health check configured, a new version must pass it before taking traffic.

App at creationDashboard defaultCLI/API default when omitted
Dynamic web app/healthNo HTTP check
Static web app//
WorkerNo checkNo check

Choose a path your app actually serves; framework conventions such as Rails’ /up are not selected automatically. An explicit path or repo-declared setting overrides the creation default. Probed instances must remain Ready for 10 seconds before the rollout treats them as available.

  • Web Applications use the health check path you configure (--health-check-path, or the equivalent dashboard field). The platform requests it from each new instance and waits for a successful response before sending it traffic. A Web Application with no health check path set gets no HTTP check: a new instance counts as healthy once it has run for 10 seconds.
  • Background Workers have no public URL, so a health check is optional. Set either a path (checked over HTTP on the port in $PORT — 8080 unless you set one) or a command (--health-check-command) that exits 0 when the worker is healthy. Without either, a new instance just has to run for 30 seconds without restarting to count as healthy.

A health check can also live in your repository’s d2o-paas.yaml instead of the dashboard or CLI. A version that sets it that way locks the field — it shows as read-only until a later version stops setting it. See Move Settings Into Your Repo.

The check keeps running after the deployment, too: an instance that fails it for about 15 seconds stops receiving traffic, and one that keeps failing for about 30 seconds is restarted.

What makes a good check: answer within a second, don’t put it behind login or any authentication your health checker won’t have, and report whether your app has started and can serve. Don’t make it fail just because a dependency is briefly unreachable — if the check queries your database and the database is down for half a minute, every instance fails at once and all of them are restarted together. See Designing Apps for d2o.

When a new version isn’t healthy

A rollout fails after 5 minutes without progress toward healthy new instances. The platform fails it sooner, without waiting out the full 5 minutes, when:

  • the container cannot start because its required configuration is unavailable — failed right away. App secret references are optional at container startup: a deleted secret can leave a variable absent instead. Validate required settings in your app;
  • it keeps exiting, after 3 restarts — reported as “Your app keeps exiting (exit code N)”, along with its recent log output — or, when the health check is what keeps failing, the check’s own result, e.g. “Health check /health returned 503”;
  • it can’t be loaded, or there isn’t enough free capacity on our servers to start it — after 2 minutes of that. A capacity failure is a problem on our side, not in your app.

Automatic rollback is an app setting — the Automatic rollback checkbox under the app’s Settings → Deployments, or --auto-rollback — on by default. When a new version fails, the platform deploys the last version that reached Live instead, and health-gates that rollback too — it has to become healthy in turn:

  • If it does, the deployment is marked Rolled back.
  • If even the previous version doesn’t come up healthy, the deployment is marked Rollback failed and the platform stops — it never flip-flops between two broken versions — and emails the organization’s owner.

Turn automatic rollback off for an app that should never silently revert on its own; an unhealthy new version then stays Rollback failed, with the previous version still running. Either way, the deployment page and paas deploys info show what happened and why.

Apps set to run at most one instance (singletons) are briefly down during a rollback: the broken instance has to stop before the previous version can start in its place.

Release command

A release command runs once against the new version, before any new instance starts — the place for a versioned migration before the app starts. Make it safe to retry; see the execution rules below.

Set it as release.command in d2o-paas.yaml, as a release: line in your app’s Procfile, or as the Release command app setting (--release-command). When more than one is present, the file wins over the Procfile, and the Procfile wins over the app setting — whichever is set furthest up that list is the one that runs, and the deployment timeline says so when the Procfile overrides the app setting:

Your app's release command setting is ignored because the Procfile defines `release:`

It runs under bash, with the same environment variables and secrets as the app itself.

  • If it exits non-zero or runs longer than 15 minutes, the deployment fails at Running release command, and the previous version keeps running, untouched.
  • It runs once per new version — a deployment, promotion, or manual rollback that changes the running version. An automatic rollback doesn’t run it again: the previous version already ran it when it went live. A deployment triggered only by a setting or environment-variable change doesn’t run it either, since the version hasn’t changed. Still, write it to be safe to run more than once: a migration tool that skips migrations it already applied is safe this way; a script that unconditionally inserts a row is not.
  • For a shared build (several apps built from one repo), it runs once — for one app, before the others start — not once per app.
  • A promotion runs the release command in the target Environment, against the target’s database.

Database migrations and rollbacks

Rollbacks restore code, not data. If a deployment’s release command already ran a migration before the deployment was rolled back, that migration is not undone — the previous code keeps running against the new schema. Write migrations so that’s safe:

  • Add first, remove later. Add a new column or table in one deployment and start writing to it; only drop the old one in a later deployment, once nothing reads it anymore.
  • Widen before you narrow. Add a new enum value (or loosen a constraint) in a deployment before any code writes it, so a rollback to the previous code isn’t left looking at a value it doesn’t understand.
  • Never edit a migration that already ran. Write a new one instead — an edited migration can behave differently on an Environment that already applied the old version of it.

For apps with a PostgreSQL database attached, every deployment whose release command runs records a data restore point — the moment right before the command started — shown on the deployment page. It marks a point you could restore the database to; restoring is a separate, deliberate action, not something a rollback does for you.

Rolling back by hand

A deployment that failed can be rolled back manually too — useful when automatic rollback is off. You need the Promote & roll back capability on the Environment.

In the dashboard, open the failed deployment’s page and click Roll back.

From the CLI:

paas deploys rollback 42

Without a number, it rolls back the Environment’s most recent deployment, if that one ended Failed or Rollback failed; when a newer deployment is live, it stops and asks for a number instead. A manual rollback of deployment N goes back to the version that was live before N. It asks for confirmation; skip that with --yes. Only the most recent deployment can be rolled back this way — for an earlier one, push a fix (or revert the commit and push that) in a build Environment, or promote the version you want in a promote-only one.

This is different from paas rollback --to <id>, which re-pins a promote-only Environment to any earlier version it has run, healthy or not — see Pipelines, Promotion & Rollback.

Build and release logs

Build and release logs stream live on the deployment page (and through paas watch, unless you pass --quiet) while they’re running; seeing them needs Deploy & operate or Promote & roll back. Afterwards, each deployment keeps its build log (the last 1 MB) for the Environment’s 1,000 most recent deployments, and its release log alongside it:

paas logs --build 42
paas logs --build 42 --release

When the deployment covers more than one app (a shared build or a push to an Environment remote), pass -a <APP> to pick whose build or release log to print — it’s required. If a log wasn’t saved and is older than 30 days, or the underlying log store is unreachable, the platform says so rather than showing nothing: “This log is no longer available.”

These are separate from your running app’s own logs — see Logs.

Next steps