Upgrading to the Latest Stack
Last updated October 04, 2024
Table of Contents
The Heroku-20 stack is deprecated and will reach end-of-life on April 30th, 2025. Please upgrade to a newer stack as soon as possible. See the Heroku-20 End-Of-Life FAQ for more details.
Introduction
This article describes how to upgrade your app to use the latest Heroku stack. Refer to the Stacks article to understand what a stack is, which stacks are available, and how to determine the stack your application is currently using.
When a Heroku app is built, the source code for your application, together with the fetched dependencies and output of the build such as the language and framework, are assembled into a bundle known as a slug. This slug will typically only be compatible with the stack on which it was built, so apps that have previously had a deploy must be rebuilt in order to be compatible with a new stack.
Changing an existing app’s stack is a very similar process to a standard app deploy. First, the stack setting of the app is updated to request a change in the target stack used for subsequent builds. Next, a new build needs to be triggered so that a new slug is created that targets/is compatible with the new stack. This new build is deployed in exactly the same way as a standard build - for example there is no downtime for the stack upgrade to occur, and rollbacks can be performed in the same way.
General Upgrading Strategy
After determining what stack your application is currently using, you must decide which stack to migrate to. In general, you should always move to the very latest available stack.
You should first review the changes between the stack you’re currently running, and the intended destination stack, as well as their respective recommendations for upgrades from an earlier stack. The Stacks article contains a list of all stacks, and the detail page for each stack linked therein contains upgrading notes.
When upgrading more than one stack generation, e.g. from Heroku-20 to Heroku-24, remember to review the upgrade notes for all intermediate stack generations to gain a comprehensive understanding of the all the changes involved.
Before upgrading your production app to a new stack, it is highly recommended you either create a test app with the new stack first (detailed in the next section), or try the upgrade with an existing staging or development app first.
Testing an App on a New Stack
You may either use a Review App to create a temporary version of your app on the desired stack, or manually create a new app that uses the desired stack and deploy your code to it.
Review Apps (Recommended)
If your organization uses Review Apps, we recommend testing a new stack by creating a branch that changes the stack definition in app.json
as follows:
{
"stack": "heroku-24"
}
Pushing this branch and creating a Pull Request for it will then deploy the app on the new stack as a Review App, where it can be thoroughly tested.
Manually Created Test App
You may also test your codebase on a different stack by creating a new test app from an existing application.
Creating a New Test App
From an existing app’s codebase, run heroku create
with arguments that create a separate test app under a different name and Git remote:
$ heroku create --remote heroku-24 --stack heroku-24 <your app name>-heroku-24
This command will:
- Create a new app named “
<your app name>-heroku-24
”; - Set the base image to the Heroku-24 stack for that newly created app;
- Set up a new Git remote named “
heroku-24
”.
Your Git repository now contains at least two remotes: heroku
, pointing to your existing production app, and heroku-24
, pointing to the new app you just created. Pushing changes to one of these apps will not impact the respective other app.
Migrating Add-ons and Config Vars
In order for your app to be fully functional, you must also mirror any add-ons and config vars.
To list your existing app’s add-ons, run:
$ heroku addons --remote heroku
For each of the add-ons listed, create a corresponding counterpart on your test app:
$ heroku addons:create --remote heroku-24 <add-on>
Next, examine your existing app’s config vars, as well as the new app’s config vars that were already set by any add-ons you may have provisioned:
$ heroku config --shell --remote heroku
…
$ heroku config --shell --remote heroku-24
…
For any config var present on your existing app that isn’t yet set on your test app, set it on the test app:
$ heroku config:set --remote heroku-24 <name>=<value>
The --shell
option of heroku config
outputs variables in a format that you can easily copy, and then paste into a heroku config:set
command line.
Deploying the Test App
Deploying your new app will not affect your currently running app.
Push the source to the test app’s remote:
$ git push heroku-24 main
Counting objects: 67, done.
...
-----> Ruby app detected
-----> Compiling Ruby/Rack
...
Once the app is deployed, you should verify that it is working correctly, and if not, make any required changes. If you encounter issues not covered in the stack upgrade notes for that stack version and need assistance, open a support ticket with the stack name in the subject line.
$ heroku open --remote heroku-24
$ heroku logs --remote heroku-24
Removing the Test App
After you have finished verifying that the test app works correctly, you can remove it like so:
$ heroku apps:destroy --remote heroku-24
Upgrading an App
Setting the Stack
To upgrade via the Heroku CLI, use the stack:set
command on your production app:
$ heroku stack:set heroku-24 -a <app name>
Setting stack to heroku-24... done
You will need to redeploy ⬢ <app-name> for the change to take effect.
Run git push heroku main to trigger a new build on ⬢ <app-name>.
For apps that have previously been built, this does not immediately change the stack of the app - a new build is required (which will target the chosen stack) for the change to take effect. For apps where code has never been deployed (such as add-on only apps), the stack change will take immediate effect and so for them a new deploy is not required.
If you have no changes to your source pending release, you can create an empty commit with no changes, to trigger a new build:
$ git commit --allow-empty -m "Upgrading to heroku-24"
[main 973c3f7] Upgrading to heroku-24
To create the new release, push to your production app:
$ git push heroku main
Your production app is now running on the latest stack, and you should verify that everything is working as expected.
Updating app.json
If you are using app.json
, you should also specify the stack there to ensure that your Review Apps and Heroku CI runs use the same stack:
{
"stack": "heroku-24"
}
An existing app’s stack cannot be changed using app.json
. The stack specified is only applied to newly created apps that are a Review App, a Heroku CI test run app, or an app created using a Heroku Buttons.
Pipeline Promotions
When using the Heroku Pipelines feature, promoting an app copies its slug to the target app and overwrites the target app’s
stack to match the stack of the originating app. As such there is no need to use stack:set
on the downstream (production) app directly. Instead, upgrade the stack of the staging app, verify the app is running as expected, and then perform a pipeline promotion as normal.
Upgrading via Dashboard
To upgrade via the Heroku Dashboard, navigate to your app settings page and click the Upgrade Stack button. Confirm that you want to upgrade, which will set the stack to the latest version.
The Upgrade Stack button is only shown if you have sufficient permissions to update the stack on the app.
For apps that have previously been built, you will see that the stack upgrade is pending; the upgrade will take effect on the next deploy. For apps where code has never been deployed (such as add-on only apps), the stack change will take immediate effect and so for them a new deploy is not required.
Rolling Back
If your app isn’t working after you changed the stack and deployed a change, you can roll back to a previous release. Rolling back to a previous release quickly restores functionality until resolve the issue by debugging or contact support.
First you need the release version of the last release that was using the previous stack. In the example v13
was the deploy after the stack change:
$ heroku releases
=== production-app Releases
v13 Deploy 973c3f7 joe@example.com 2024/06/12 10:55:16
v12 Deploy ddb317d jill@example.com 2024/06/09 15:33:59
...
Then roll back to the previous working release. In the example, that’s v12
:
$ heroku rollback v12
Your production app is now again running on the previous stack.
Alternatively, if you would prefer not to use the rollback feature, you can change the stack back using heroku stack:set <original-stack-name>
and then complete the stack downgrade by performing another deploy.
Common Issues
Old Buildpack Versions
The buildpacks defined for an app can be specified using several different forms of buildpack reference. Pinning to a specific buildpack version tag/branch/SHA will mean any compatibility fixes for new stacks won’t be picked up automatically, which may cause the build to fail on newer stacks.
List the current buildpacks using:
$ heroku buildpacks
If you see any buildpack URLs ending with a #
followed by a Git reference (e.g. a commit SHA, branch name or tag name), these should be be replaced with a buildpack URL that does not contain the version suffix. See buildpack references for more details.
Third-Party Buildpacks
We expect most third-party buildpacks will work with a new stack. If your app is using a third-party buildpack, and you experience an issue building your app, we recommend clearing the build cache:
$ heroku plugins:install heroku-builds
$ heroku builds:cache:purge
If clearing the build cache doesn’t work, we suggest opening up an issue on the buildpack’s GitHub page.
Stack-Specific Issues
See the “What’s new” and “Upgrade notes” sections on each stack’s detail page, for commonly encountered issues that are specific to that stack: