deplaneDocs Dashboard

Release command

A release command runs once before each new version starts serving, usually to migrate the database. If it fails, the new version is not released and the one that is live keeps serving.

Set it

Set it in any of three places:

Setting the command does not deploy anything. It takes effect with the next deploy. The command is one line of at most 1000 characters, and it runs through the image's shell (/bin/sh -c), so && and environment variables work.

When it runs

It runs for a release that comes from a new build: an upload with deplane deploy, a GitHub push, a template, or a Docker image. A deploy that produces exactly the image that is live does not run it again, unless the last run of the command failed.

It does not run for:

A release never runs its command twice. Pull request previews run the same command as the app they belong to, against their own database. A preview's own release command cannot be set: release_command_on_preview.

While it runs

The app shows Deploying with the message "Running the release command". When your team's two slots for runs and release commands are busy, the command first waits for one, and deplane deploy says "Waiting for a free slot to run the release command." and keeps waiting, up to an hour. The live version keeps serving, and nothing that starts instances changes: scaling, workers and cron jobs keep what they have until the command ends, for up to 15 minutes. Variables, domains and alerts apply as usual. If a request wakes a sleeping app in the meantime, it is answered once the command ends.

The command runs in the new release's code with the same variables, DATABASE_URL and REDIS_URL as your app, at the app's size, with no disk and no inbound traffic. It has 15 minutes. Its output appears in deplane logs --process release and on the app's Logs tab, named release.

deplane deploy waits for it, prints "Running the release command...", and allows 15 more minutes for the rollout.

StateMeaning
pendingThe command is waiting to run.
runningIt is running now.
succeededIt finished with exit code 0, and the release went on to roll out.
failedIt exited with another code, ran past 15 minutes, or never got room to start.
skippedIt did not need to run for this release.

deplane releases shows the state in a RELEASE COMMAND column, and the Deployments tab shows it beside each release.

When it fails

The release is marked failed with the reason ReleaseCommandFailed, and the previous release keeps serving. A first release has no previous one, so the app stays not deployed. The last 20 lines of the command's output, at most 2 KB, are kept with the release. deplane deploy prints them and exits with code 4, and the Deployments tab shows them under the release.

$ deplane deploy
...
Built 4f1c2a9e8b7d. Rolling out release v4...
Running the release command...
The release command failed. Its last output:
Running migration 0007
ERROR: relation "widgets" does not exist
Error: Release v4 was not released because its release command failed. v3 keeps serving.
Fix: Read the output above, fix the command or the code it runs, and run `deplane deploy`. The release command runs again with the next deploy, so make it safe to run twice.

Fix the migration and deploy again. The next deploy runs the command again, even with the same image.

Make it safe to run twice

A migration that stopped halfway, or a deploy that is superseded by a newer one while the command runs, runs again. Write migrations so that a second run is harmless: most migration tools, such as Django, Rails, Prisma, Alembic and Flyway, record what they applied and are safe as they are. Hand-written SQL needs IF NOT EXISTS, upserts, or a check first.

A deploy while another release command is running replaces it: the older command is stopped and the new release's command starts.

Limits

LimitValue
Time the command may run15 minutes.
Release commands and runs at once, for your team2. Past that, run_busy.
SizeThe app's size.
Output kept with a failed releaseThe last 20 lines, at most 2 KB.

Examples

StackRelease command
Djangopython manage.py migrate --noinput
Railsbin/rails db:migrate
Node with Prismanpx prisma migrate deploy
Node, a script of yoursnpm run migrate
Several stepspython manage.py migrate --noinput && python manage.py collectstatic --noinput

This page is also available as Markdown. AI agents can start at /llms.txt.