n.

How to Deploy a Node app to Heroku as a Docker container

Deploying a Node app to Heroku is incredibly simple. However deploying it as a Docker image requires a couple of extra steps.

Pre-requisites

Needless to say, before following the steps below, you'll need to have Node.js and Docker installed on your machine, along with a basic understanding of both. You'll also need a Heroku account and the Heroku CLI installed.

The example project used in this article has the following structure:

app-name
  ├── Dockerfile
  ├── index.js
  ├── heroku.yml
  ├── package.json
  └── ...

First, we'll need a Node.js application to deploy! To demonstrate, I've used the Express Hello, World example.

// index.js

const express = require('express')
const app = express()
const port = 8080

app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`)
})

Creating a Dockerfile

I've put together a basic Dockerfile which will install our dependencies and run our Node application.

The following Dockerfile will:

  1. Use Node 12 Alpine as its base
  2. Set the mainter label
  3. Set a an environment variable of production
  4. Set an environment variable to an incoming variable $PORT
  5. Copy everything from the project root into a /var/www directory in the container
  6. Set the container work directory to where the files are
  7. Install the Node dependencies
  8. Expose itself to access on the given port
  9. Run npm start
# Dockerfile

FROM        node:12-alpine

LABEL       maintainer="<Your name>"

ENV         NODE_ENV=production
ENV         PORT=$PORT

COPY        . /var/www
WORKDIR     /var/www

RUN         npm install

EXPOSE      ${PORT}

CMD         [ "npm", "start"]

To build the Docker image, we run the docker build command1 and tag the image with the name of the app:

docker build -t <app-name> .

then to run the image, we use the docker run command2, and pass in two parameters:

  • -p - Exposes port 8080 inside the container, to the public port 8080 through which we can access the application.3
  • -e - Sets the environment variable specified in the Dockerfile. In this case, the port number.4
docker run -p 8080:8080 -e PORT=8080 <app-name>

the application should now be running on http://localhost:8080/.

Setting a Heroku app to run a container

If you already have an app running in Heroku, you can change the stack form the default heroku-x to container using the Heroku CLI3:

heroku stack:set -a <app-name> container

You can verify that the stack has changed either in the portal, or with the CLI, by running:

heroku apps:info -a <app-name>

You should see a list of values including Stack: container.

Deploying and running the container

When deploying your application in a container, Heroku requires that your project contains a heroku.yml file4. These manifest files allow you to instruct Heroku how to build, configure and run your containers. Whilst these configurations are incredibly useful in complex applications, I'd wager that the vast majority of Web applications just require a simple run command:

build:
  docker:
    web: Dockerfile

This configuration file appears very simple because there's a couple of things happening behind the scenes.

Firstly, as we haven't specified a run section in the yaml file, Heroku will just use the CMD specified in our Dockerfile.

Secondly, because Heroku automatically assigns the ports when you deploy, there's no need for us to pass any of the parameters we do when working locally. We just allow Heroku to provide the port numbers to the application.

And that's it! Push your code changes to your repository, and Heroku will take care of the rest.


References

  1. Docker build reference
  2. Docker run reference
  3. Docker run reference - Publish or expose port
  4. Docker run reference - Set environment variables
  5. Heroku CLI Commands
  6. Building Docker Images with heroku.yml