Recently, we worked on deploying a legacy Rails app to Heroku. The minimum Ruby version that Heroku supports is 2.7.6. Apps running older versions must be upgraded before they can be deployed. Thankfully, this problem can be solved by deploying the app as a container. This post explains how to containerize an application and deploy it to Heroku.
Dockerize the application
First, we need to create a simple Dockerfile for the application:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
FROM ruby:2.4.2
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
COPY . /myapp
EXPOSE 3000
CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]
Different Ruby apps will have different Dockerfiles. This example was built for a simple Rails 4 app running Ruby 2.4.2 but it can be used as a starting point and modified for different use cases.
With this Dockerfile, the app can be run locally:
1
docker build -t myapp . && docker run -it myapp
There may be errors, when running the app. This is common when containerizing a legacy app, there are often dependencies which need to be fixed or upgraded.
Adding Heroku.yaml
Once the app is running locally, we need to add a heroku.yaml
file:
1
2
3
4
5
6
7
build:
docker:
web: Dockerfile
config:
RAILS_ENV: production
run:
web: bundle exec rails server -b 0.0.0.0 -p $PORT
This tells Heroku to build an image using the Dockerfile and then specifies a process for running the container.
Notice that the web process is running on a port specified by $PORT
. This is required because Heroku dynamically assigns a port, on each deploy, which the web process must listen on.
Building an app and deploying
At this point we can build a new Heroku app, either via the UI or CLI. By default Heroku apps do not run container builds. We must change the stack via the Heroku CLI:
1
heroku stack:set container
The codebase and Heroku app are now ready, we can push to Heroku to start the build and deployment.
Switch over DB and copy over environment variables
The app has been deployed successfully and is running on Heroku but we’re missing important components such as a database. There are two options here:
- Migrate addons from the legacy Heroku app to the new app
- Copy Config Vars from the legacy app to the new app, reusing the legacy app’s add-ons
Option one is cleaner and would allow us to completely decommission the old application. Option two is faster.
We recommend starting with option 2 in order to verify that the new app is working correctly. Then gradually migrate add-ons across.