Deploy Docker To Heroku

By | May 28, 2018

Heroku is a ‘platform as a service’ (PaaS) cloud provider. Heroku makes it easy to deploy apps by pushing to git repositories or using docker containers. This post shows you how you can deploy docker to Heroku.

The Heroku website already has some good guides to help you deploy docker to Heroku, but I found there were some things that didn’t quite make sense to me.

In this post I build on my previous post which showed how to make a simple flask app in a docker container. In that post we made a simple app which we could access in a locally hosted docker container. If we want others to be able to access it it we need to deploy it or host it elsewhere.

Get Set Up With Heroku

Heroku Basics

To follow along with the rest of this post you’ll need to create a heroku account and install the heroku cli app.

Install the Heroku Docker Plugin

Docker is supported in Heroku, but you need to install a plugin first. You can do this by running this command:

heroku plugins:install @heroku-cli/plugin-container-registry

Then start up docker on your local machine, and run (make sure you have already logged into heroku CLI):

 heroku container:login 

Create a Heroku App

Our container will need to run from within a heroku app. If you don’t already have one, create a new app with this command:

 heroku create 

Prepare Your Docker Container

You will need to make some alterations to your docker container and your app to prepare it to be hosted on Heroku. To illustrate the kind of things you need to consider I will modify my existing simple flask app.

Checkout the example heroku docker app on github.

Change Docker User

Heroku runs docker apps a a non-root user. To help when it comes to testing locally, it is recommended to add the follow lines to your dockerfile, before the ‘CMD’ command that runs the app script:

 
RUN adduser -D myuser
USER myuser 

Port Forwarding

Heroku is all about running web apps. It takes requests from the internet and passes them to a particular port on the Heroku app. The way Heroku is configured, the port the our app should listen to is set by the $PORT environment variable in the Heroku app.

As far as Heroku is concerned, the docker container is the web service, and needs to be listening on the port set by the $PORT environment variable.

Running locally you can expose which ever port you want, and you would typically expose the port needed by the process or service running within the container (e.g. port 5000 is the default port for a Flask app).

But Heroku requires that the web process (i.e. the docker container) listens on a particular port specified by the $PORT environment variable. This is the only port open for use, and Heroku will throw an error if the running service does not connect to that port within 60 seconds.

Behond the scenes, heroku must be running something like

docker run -p 80:$PORT -e PORT=$PORT where ’80’ is the port heroku exposes to the internet, and $PORT is the environment variable Heroku sets when an app is created. Running this would the correct port in the docker container when it is run. So far so good.

This is fine, but our web app will not work unless it knows that it should not be listening on it’s default port, but instead on this new port that docker is expecting to open up. The final peice of the puzzle, then, is to configure the service or process running on the docker container to also use the $PORT environment variable.

For my simple Flask app inside a docker container I needed to add the port argument to ‘app.run’ in the flask script, and use the ‘os.environ.get()’ to capture the envioronment variable:

 app.run(debug=True, host='0.0.0.0', port=os.environ.get('PORT')) 

Test Your Container Locally

Changing the docker container to be suitable for Heroku also changes how we run it locally. In particular, our app now expects a $PORT environment variable, and we need to make sure the docker container exposes that port.

First we can set the PORT environment variable.

When we run docker we need to tell it to create an environment variable ‘PORT’ which will be picked up by the

docker run -p 5000:5000 -e PORT=5000 <image-name> 

Or configure a docker-compose script to forward the appropriate ports, and create an environment variable within the docker container:

version: '2'
services:
    web:
        build: .
        ports:
            - "5000:5000"
        volumes:
            - .:/web
        environment:
- PORT:5000

You can then run your local, development version with this command:

 docker-compose run --service-ports web 

Read more about heroku and docker compose.

Push Your Container to Heroku

Pushing your docker container to heroku is quite simple.

 heroku container:push web --app HEROKU_APP_NAME 

Try Out Your App

Run your new app with this command:

heroku open --app ${YOUR_APP_NAME}

This will open up your app url in a browser window.

View at Medium.com