Avatar

How to deploy an application to Heroku using Docker

โ† Back to list
Posted on 17.07.2019
Image by Samuel Zeller on Unsplash
Refill!

Personally, I am extremely fascinated with a container-based approach. I try to ship every application as a Docker container because in this case, the application becomes an infrastructure provider agnostic. So this time we will see how to deploy a dockerized application to Heroku โ€” a famous cloud platform.

Step 1: The Code #

My applicationโ€™s code is as simple as 5 cents:

๐Ÿ‘‰ ๐Ÿ“ƒย ย index.js
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.status(200).send('Hello');
});
const port = process.env.PORT || 3000;
app.listen({ port }, () => {
console.log(`๐Ÿš€ Application is ready at http://localhost:${port}`);
});
The code is licensed under the MIT license

It is written in NodeJS, but it really does not matter, it could be anything else.

โšก The first important thing: internally Heroku chooses a random port to hang the container on, so your application should look at PORT environment variable.

Let's create a couple of files and put them next to the application:

๐Ÿ‘‰ ๐Ÿ“ƒย ย Dockerfile
FROM node:11
RUN apt-get update && apt-get install -y --no-install-recommends vim && apt-get clean
WORKDIR /app
COPY . .
RUN yarn
ENV NODE_ENV=production
CMD [ "yarn", "start" ]
The code is licensed under the MIT license
๐Ÿ‘‰ ๐Ÿ“ƒย ย package.json
{
"name": "poc_docker-heroku",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"keywords": [],
"author": "",
"license": "UNLICENSED",
"dependencies": {
"express": "^4.17.1"
}
}
The code is licensed under the MIT license

Step 2: Authentication #

From this moment on I assume that we have already signed up at heroku.com and created our application. Let's say, it will be my-first-unique-app (if this particular name was already taken, pick the other one). Heroku platform offers a really generous free plan which is more than enough not only for experimenting but for building an actual application that may go live.

We are going to need for Heroku CLI tool. The easiest way to get it (from my perspective) is to install it with curl:

$
curl https://cli-assets.heroku.com/install.sh | sh;
The code is licensed under the MIT license

Next thing: authenticate through the CLI tool in the interactive mode, using email and password you created while signing up at heroku.com:

$
heroku login -i
The code is licensed under the MIT license

This command will create a record in your ~/.netrc file.

There are two ways of how to proceed further. Originally, Heroku was made with the idea of strong bonding between itself and GitHub, so they offer to build a Docker image on their side after we commit to the GitHub repository. Alternatively, we can build an image on our side and then just push it to their own Docker image repository.

From my point of view, the second option gives more flexibility, so we will proceed this way.

Step 3: Deployment #

Officially, we are supposed to run the following commands in order to build an image, and then push it to Heroku's own docker repository:

$
heroku container:login;
heroku container:push web -a my-first-unique-app --context-path=./;
The code is licensed under the MIT license

But, there are limitations (so far):

  • the second command should be executed in the same folder where Dockefile is located,
  • the file should be named as Dockerfile or Dockerfile.web, which is not cool in some situations.

If we run the second command with a -v flag, we won't fail to notice, that this one is basically an alias for a sequence of docker commands. So, why don't we exploit an advantage of using flexible Docker CLI tool itself?

First of all, we authenticate on Heroku's own repository with this superposition of commands:

$
docker login --username=_ --password=`heroku auth:token 2> /dev/null` registry.heroku.com
The code is licensed under the MIT license

The heroku auth:token command returns the current token we got with heroku loginย . Username _ should stay like this (weird, but true). Now, we build an image:

$
docker build -t registry.heroku.com/my-first-unique-app/web -f ./Dockerfile .
The code is licensed under the MIT license

Here we are free to choose the Dockerfile to build from, as well as the build context and other options. The tag name should stay like this, there is no need to change it. The web code indicates that this particular container should be automatically exposed to the outer world through a technical domain. Hopefully, if the image is built with no errors, we can push it:

$
docker push registry.heroku.com/my-first-unique-app/web
The code is licensed under the MIT license

As soon as the image is there, we can finally put it live by calling

$
heroku container:release web -a my-first-unique-app
The code is licensed under the MIT license

Let's give it some time to spin up and go have a coffee break. When we are back, visit https://my-first-unique-app.herokuapp.com/

Hopefully, we should see "Hello" messageย :)

Well, that is basically it. If something goes wrong, it is possible to view logs by typing

$
heroku logs --tail -a my-first-unique-app
The code is licensed under the MIT license

As usual, here is a proof-of-concept repository at your service. Enjoy!

Useful links:

Happy container-ing!


Avatar

Sergei Gannochenko

Business-oriented fullstack engineer, in โค๏ธ with Tech.
Golang, React, TypeScript, Docker, AWS, Jamstack.
15+ years in dev.