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:
- the app's Settings tab in the dashboard, under Release command;
- the CLI:
deplane config --release-command "python manage.py migrate", anddeplane config --release-command ""to remove it; deplane.toml, under[service]:release_command = "npm run migrate". Eachdeplane deploysends it when it differs from the app's. Leaving the line out does not remove the command.
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 change of variables, or attaching or detaching a database or Redis, because the code is the same;
- a rollback, or an automatic rollback, because old code must not run old migrations.
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.
| State | Meaning |
|---|---|
pending | The command is waiting to run. |
running | It is running now. |
succeeded | It finished with exit code 0, and the release went on to roll out. |
failed | It exited with another code, ran past 15 minutes, or never got room to start. |
skipped | It 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
| Limit | Value |
|---|---|
| Time the command may run | 15 minutes. |
| Release commands and runs at once, for your team | 2. Past that, run_busy. |
| Size | The app's size. |
| Output kept with a failed release | The last 20 lines, at most 2 KB. |
Examples
| Stack | Release command |
|---|---|
| Django | python manage.py migrate --noinput |
| Rails | bin/rails db:migrate |
| Node with Prisma | npx prisma migrate deploy |
| Node, a script of yours | npm run migrate |
| Several steps | python manage.py migrate --noinput && python manage.py collectstatic --noinput |
This page is also available as Markdown. AI agents can start at /llms.txt.