Upgrade a Rails app from the Heroku-16 to the Heroku-18 Stack
Tags: rails, ruby, heroku, devops
Published: 2021-05-30 00:44:42

I'm working with a client to urgently upgrade their Rails app from the Heroku-16 stack to the Heroku-18 stack. Heroku has already end-of-life'd the Heroku-16 stack at the beginning of this month. And in a few days, on June 1st, they will completely turn off support patches and new builds of Heroku-16 apps.

The Heroku-18 stack is supported until April 2023 ([source](https://devcenter.heroku.com/articles/stack)).

The minimum Ruby version supported by the Heroku-18 is Ruby 2.4.10 ([source](https://devcenter.heroku.com/articles/ruby-support#ruby-versions)).

### Testing the Ruby Upgrade Locally

Install the target Ruby version:

```shell
$ asdf install ruby 2.4.10
```

Switch to that Ruby version:

```shell
$ asdf local ruby 2.4.10
$ ruby --version
ruby 2.4.10p364 (2020-03-31 revision 67879) [x86_64-darwin19]
```

Install an older version of bundler (pre 2.x):

```shell
$ gem install bundler:1.17.3
```

Install all the app's gems with for the new Ruby version using the specific version of bundler:

```shell
$ bundle _1.17.3_ install
```

Start up the server locally (connected to whatever DB makes sense) and try things out:

```shell
$ bundle exec rails s
```

### Deploying a Preview App

A Preview App is a great way to get this app up and running in a production-like environment. This is an opportunity to further kick the tires of the app. Are there particularities of the production environment and settings or of Heroku that make this app not work? If so, it is best to find out here rather than in production.

This will involve deploy a branch with a new Ruby version to a Preview app on a newer Heroku stack.

Here is a Heroku guide on [upgrading the stack of an app](https://devcenter.heroku.com/articles/upgrading-to-the-latest-stack).

The [recommended way](https://devcenter.heroku.com/articles/upgrading-to-the-latest-stack#testing-an-app-on-a-new-stack) to do this is to create a branch for your app, alter the stack in `app.json` and then deploy a preview app (actually called Review Apps) to Heroku. It will use the stack version specified in that JSON file.

```javascript
{
  "stack": "heroku-18"
}
```

### Deploying the Changes to Production

I initially tried to push the Ruby upgrade changes and the `app.json` Stack changes directly to production. This led to the Ruby upgrade changes being deployed on the old stack. Despite the changes made to `app.json`, Heroku doesn't upgrade the stack for an existing app that way.

> 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. [source](https://devcenter.heroku.com/articles/upgrading-to-the-latest-stack#updating-app-json)

Fortunately, the Ruby upgrade changes deployed fine to the existing stack. And the app was able to build and deploy the existing stack because we are still 2 days out from the full deprecation.

How I should have done it was to first set the stack for the app using the Heroku CLI.

```shell
$ heroku stack:set heroku-18 -a <app name>
```

Heroku will apply that stack upgrade on the next build. So once that is set, I should have then pushed up the Ruby upgrade changes.

Because I did it out of order, I had to trigger another production deploy by pushing up an empty commit.

```shell
$ git commit --allow-empty -m "Upgrading to heroku-18"
```

Once that empty commit is on the main branch, I then pushed that directly to the remote's main branch (`git push origin main`). This works because I have GitHub integrated with Heroku so that any changes pushed to the `main` branch will trigger a deploy. If that wasn't the case, you can trigger a deploy the Heroku way by pushing to the Heroku remote (`git push heroku main`).

_If you enjoy my writing, consider [joining my newsletter](https://crafty-builder-6996.ck.page/e169c61186) or following me on [twitter](https://twitter.com/jbrancha)._

---

<em>Cover photo by <a href="https://unsplash.com/@charlotablunarova?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Charlota Blunarova</a> on <a href="https://unsplash.com/s/photos/neon?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></em>