Deep-dive on the Next Gen Platform. Join the Webinar!

Skip Navigation
Show nav
Dev Center
  • Get Started
  • Documentation
  • Changelog
  • Search
  • Get Started
    • Node.js
    • Ruby on Rails
    • Ruby
    • Python
    • Java
    • PHP
    • Go
    • Scala
    • Clojure
    • .NET
  • Documentation
  • Changelog
  • More
    Additional Resources
    • Home
    • Elements
    • Products
    • Pricing
    • Careers
    • Help
    • Status
    • Events
    • Podcasts
    • Compliance Center
    Heroku Blog

    Heroku Blog

    Find out what's new with Heroku on our blog.

    Visit Blog
  • Log inorSign up
View categories

Categories

  • Heroku Architecture
    • Compute (Dynos)
      • Dyno Management
      • Dyno Concepts
      • Dyno Behavior
      • Dyno Reference
      • Dyno Troubleshooting
    • Stacks (operating system images)
    • Networking & DNS
    • Platform Policies
    • Platform Principles
  • Developer Tools
    • Command Line
    • Heroku VS Code Extension
  • Deployment
    • Deploying with Git
    • Deploying with Docker
    • Deployment Integrations
  • Continuous Delivery & Integration (Heroku Flow)
    • Continuous Integration
  • Language Support
    • Node.js
      • Node.js Behavior in Heroku
      • Working with Node.js
      • Troubleshooting Node.js Apps
    • Ruby
      • Rails Support
      • Working with Bundler
      • Working with Ruby
      • Ruby Behavior in Heroku
      • Troubleshooting Ruby Apps
    • Python
      • Working with Python
      • Background Jobs in Python
      • Python Behavior in Heroku
      • Working with Django
    • Java
      • Java Behavior in Heroku
      • Working with Java
      • Working with Maven
      • Working with Spring Boot
      • Troubleshooting Java Apps
    • PHP
      • Working with PHP
      • PHP Behavior in Heroku
    • Go
      • Go Dependency Management
    • Scala
    • Clojure
    • .NET
      • Working with .NET
  • Databases & Data Management
    • Heroku Postgres
      • Postgres Basics
      • Postgres Getting Started
      • Postgres Performance
      • Postgres Data Transfer & Preservation
      • Postgres Availability
      • Postgres Special Topics
      • Migrating to Heroku Postgres
    • Heroku Key-Value Store
    • Apache Kafka on Heroku
    • Other Data Stores
  • AI
    • Working with AI
    • Heroku Inference
      • AI Models
      • Inference Essentials
      • Inference API
      • Quick Start Guides
  • Monitoring & Metrics
    • Logging
  • App Performance
  • Add-ons
    • All Add-ons
  • Collaboration
  • Security
    • App Security
    • Identities & Authentication
      • Single Sign-on (SSO)
    • Private Spaces
      • Infrastructure Networking
    • Compliance
  • Heroku Enterprise
    • Enterprise Accounts
    • Enterprise Teams
    • Heroku Connect (Salesforce sync)
      • Heroku Connect Administration
      • Heroku Connect Reference
      • Heroku Connect Troubleshooting
  • Patterns & Best Practices
  • Extending Heroku
    • Platform API
    • App Webhooks
    • Heroku Labs
    • Building Add-ons
      • Add-on Development Tasks
      • Add-on APIs
      • Add-on Guidelines & Requirements
    • Building CLI Plugins
    • Developing Buildpacks
    • Dev Center
  • Accounts & Billing
  • Troubleshooting & Support
  • Integrating with Salesforce
  • Continuous Delivery & Integration (Heroku Flow)
  • Release Phase

Release Phase

English — 日本語に切り替える

Last updated April 30, 2025

Table of Contents

  • Specifying Release Phase Tasks
  • When Does the release Command Run?
  • Release Command Failure
  • Checking Release Status & Logs
  • Canceling a Release Command
  • Review Apps and the Postdeploy Script
  • Design Considerations
  • Known Issues

The release phase enables certain tasks to be run before a new release of an app is deployed. The release phase can be useful for tasks such as:

  • Sending CSS, JS, and other assets from the app’s slug to a CDN or S3 bucket
  • Priming or invalidating cache stores
  • Running database schema migrations

If a release phase task fails, the new release is not deployed, leaving the current release unaffected.

When using the release phase, there are a number of design considerations to take into account, especially if performing database migrations. The release phase has a 1-hour timeout, and this limit cannot be extended.

Specifying Release Phase Tasks

To specify the tasks to run during the release phase, define a release process type in the app’s Procfile. For deploying Docker images to Heroku, learn more about using the release phase with Container Registry.

In this Procfile example, release executes a Django database migration:

release: python manage.py migrate
web: gunicorn myproject.wsgi

Whereas in this example, release runs a script that includes several different commands:

release: ./release-tasks.sh
web: gunicorn myproject.wsgi

When Does the release Command Run?

The release command runs in a one-off dyno whenever a new release is created, unless the release is caused by changes to an add-on’s config vars. All of the following events create a new release:

  • A successful app build
  • A change to the value of a config var (unless the config var is associated with an add-on)
  • A pipeline promotion
  • A rollback
  • A release via the platform API
  • Provisioning a new add-on

App dynos don’t boot for a new release until the release phase finishes successfully:

Release phase diagram

Use the heroku ps command to see your release command running.

The dyno type can be set using `heroku ps:type release={type}, but only after the release phase is run for the first time.

Release Command Failure

If the release command exits with a non-zero exit status, or if it’s shut down by the dyno manager, the release fails. In this case, the release is not deployed to the app’s dyno formation. An email notification is generated in the event of a release phase failure.

If a release is triggered by a change to the value of a config var, the config var value remains changed even if the release command fails.

It is possible for a build to succeed and its associated release to fail. This does not clear the build cache.

A failed release command usually requires a fix to an app’s code. After making the necessary changes, push the new code to trigger a new release.

In some cases, a release failure is unrelated to the app’s code. For example, an external service might be unavailable during the release phase. In such occurrences, run heroku releases:retry to retry a failed release without needing to trigger a new build on the app.

Checking Release Status & Logs

To check on the status of a release, including failed releases and pending releases due to a long-running release command, run heroku releases.

$ heroku releases
=== limitless-savannah-19617 Releases - Current: v52
v53  Deploy ad7c527 release command failed        [email protected]
v52  Deploy b41eb7c                               [email protected]
v51  Deploy 38352d3                               [email protected]
...

To see the output of a particular release command, use the heroku releases:output command:

$ heroku releases:output RELEASE_NUMBER
--- Migrating db ---
INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.

Release logs are also available from the Heroku dashboard:

Screenshot of release logs

Heroku automatically updates any pending releases to failed after 24 hours.

Checking Release Status Programmatically

To check the status of a release programmatically, query the Platform API for a specific release or a list of all releases. See the Platform API documentation for more details. In the following example using curl, the status key has a value of failed:

$ curl -n https://api.heroku.com/apps/<app_id_or_name>/releases/ \
          -H "Accept: application/vnd.heroku+json; version=3"
{
  "app":{
    "id":"a933b6af-b2e3-4a03-91a9-1c758110a553",
    "name":"limitless-savannah-19617"
  },
  "created_at":"2016-07-29T22:43:20Z",
  "description":"Deploy ad7c527",
  "status":"failed",
  "id":"735a9e8c-ef49-4047-8079-984d40a84051",
  "slug":{
    "id":"a49ed40f-801a-45ff-8b90-758c5a33637f"
  },
  "updated_at":"2016-07-29T22:43:20Z",
  "user":{
    "email":"[email protected]",
    "id":"cbcd34ce-c556-4289-8bc7-2dc160649fb7"
  },
  "version":53,
  "current":true,
  "output_stream_url":"https://release-output.heroku.com/streams/a9/...",
  "eligible_for_rollback": false
}

The output of the release is available under the output_stream_url attribute and can be retrieved programmatically by making a GET request on the URL.

The output is sent via chunked encoding, and the connection is closed when the command completes.

If a client connects after data is sent for a given URL, the data is buffered from the start of the command. Output can be streamed while the command is in progress, and at any time after it has completed (in the latter case, all output will be sent immediately).

Canceling a Release Command

To cancel a release command, identify the one-off dyno executing the command:

$ heroku ps
=== release (Eco): bundle exec rake db:migrate
release.5129: up 2016/02/01 12:17:45 (~ 2s ago)

Then, provide the name of the one-off dyno to the heroku ps:stop command:

$ heroku ps:stop --dyno-name release.5129

Review Apps and the Postdeploy Script

Review apps run the code in any GitHub pull request in a complete, disposable Heroku app. Review apps support a postdeploy script, which is used to run one-time setup tasks.

The following timeline illustrates the order of operations and a recommended division of tasks when using both the release phase and a postdeploy script for review apps.

With each new pull request, review app creation begins.

  • After a successful build, the release command runs. This is recommended for:
    • Database schema setup and migrations
    • CDN uploads
    • Cache invalidation and warmup
  • If release phase fails, review app creation also fails

After the release phase succeeds:

  • The postdeploy script runs. This is recommended for one-off tasks, such as:
    • Setting up OAuth clients and DNS
    • Loading seed or test data into the review app’s test database

On any subsequent changes to the pull request:

  • The release command runs again.
  • The postdeploy script does NOT run again. The postdeploy runs only once on creation of the review app.

The recommendations in this section also apply to Deploy to Heroku button apps that use a postdeploy script.

Design Considerations

Minimize the amount of time a failed release is most recent

In the case where a build has succeeded, but the release phase failed, the built slug will be used by any later releases triggered by add-on config var changes. As those releases do not run the release phase, this can mean application code deploys without release phase tasks it depends on (database migrations, for example) being run. We recommend any release phase failures are resolved as soon as possible to avoid this (for example, by temporarily rolling back to a previous release).

Don’t use the release phase for tasks requiring file system persistence

The release phase is not suitable for tasks that require filesystem persistence, as filesystem changes during the release phase will not be deployed to your app’s dyno formation (the dyno filesystem is ephemeral). For example, asset compilation should happen during builds. The release phase then can be used to upload the compiled assets to a CDN.

The following considerations are primarily relevant when creating manual database migration scripts. If using an ORM, such as ActiveRecord, these considerations may not apply. Learn more about database best practices.

Use transactions for database migrations

When performing a database migration, always use transactions. A transaction ensures that all migration operations are successful before committing changes to the database, minimizing the possibility of a failed partial migration during the release phase. If a database migration fails during the release phase (i.e., the migration command exits with a non-zero status), the new release will not be deployed. If transactions were not used, this could leave the database in a partially migrated state. We suggest using heroku run, rather than the release phase, to make schema/data corrections.

Check whether a database has already been migrated before executing a migration

Many actions create a new release, such as setting a config var or adding a new addon to the app. The database migration script should check whether a database has already been migrated before executing a new migration (e.g., does table/column exist, if not add it). Doing so will prevent a new release – such as one created from a new config var – from rerunning database migrations.

Before running a migration, grab an advisory lock on the database

Heroku releases can run concurrently, which can be a problem if the release phase is executing a database migration. Many popular relational databases, such as Postgres and MySQL, offer advisory lock functionality, which can be used to prevent concurrent migrations. Advisory locks are application enforced database locks; when acquired, the tables are not locked for writing, so the application will continue to behave normally.

Postgres makes it possible to acquire an advisory lock before running migrations. In the example below, we try to get an advisory lock with the key migration:

SELECT pg_try_advisory_lock(migration);

If the lock is successful, Postgres will return t. With a lock in place, it is now safe to run migrations. If unsuccessful, f is returned, instead, and migrations are safe to fail.

If the advisory lock is acquired within a transaction, it will be automatically released when the transaction is committed. Locks can be released manually using:

SELECT pg_advisory_unlock_all();

or

SELECT pg_advisory_unlock(key);

Ensure wrapped release phase tasks have their exit status properly rolled up

In the case of a release phase where multiple tasks are wrapped into a single file (e.g., release-tasks.sh), ensure each of those tasks has its exit status captured. If the captured status is not 0 (zero), the wrapper is responsible for adequately conveying it. Not capturing exit statuses for any wrapped commands will see their statuses swallowed, and the release phase will report as successful merely because of the successful invocation of release.sh.

A simple example of this behavior would look like:

# release-tasks.sh

## Step to execute
bundle exec rails db:migrate

# check for a good exit
if [ $? -ne 0 ]
then
  # something went wrong; convey that and exit
  exit 1
fi

# other code, potentially

Known Issues

  • It’s possible for a release command to fail due to an add-on not being fully provisioned when the command is run. For example, database migrations are triggered, but the database add-on isn’t finished with provisioning. This issue does NOT impact Heroku Postgres or Heroku Key-Value Store.

Keep reading

  • Continuous Delivery & Integration (Heroku Flow)

Feedback

Log in to submit feedback.

Releases Releases

Information & Support

  • Getting Started
  • Documentation
  • Changelog
  • Compliance Center
  • Training & Education
  • Blog
  • Support Channels
  • Status

Language Reference

  • Node.js
  • Ruby
  • Java
  • PHP
  • Python
  • Go
  • Scala
  • Clojure
  • .NET

Other Resources

  • Careers
  • Elements
  • Products
  • Pricing
  • RSS
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku Blog
    • Heroku News Blog
    • Heroku Engineering Blog
  • Twitter
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku
    • Heroku Status
  • Github
  • LinkedIn
  • © 2025 Salesforce, Inc. All rights reserved. Various trademarks held by their respective owners. Salesforce Tower, 415 Mission Street, 3rd Floor, San Francisco, CA 94105, United States
  • heroku.com
  • Legal
  • Terms of Service
  • Privacy Information
  • Responsible Disclosure
  • Trust
  • Contact
  • Cookie Preferences
  • Your Privacy Choices
OSZAR »