Heroku makes it dead simple to deploy and operate apps; Docker makes it even easier! Heroku has a Docker runtime in beta.

This article will review how to setup a simple Ruby app to build and deploy as a Docker container on Heroku using CircleCI.

The app we’ll be using

We run a dashboard for internal use with a range of widgets that give us data about various events in the company and application. We use Smashing, which is a Sinatra-esque Ruby framework for building dashboards.

Setting up the Dockerfile

Heroku recently released their new Heroku-16 stack. We’ll base our image off it. We’ll then install required system dependencies so we can install gems. Next we’ll install bundler and our gem dependencies. From there we’ll give the container a default CMD that will boot up the dashboard.

FROM heroku/heroku:16

RUN apt-get update -qq && apt-get install -y \
  build-essential \
  libpq-dev \
  nodejs \
  ruby-dev
RUN mkdir /my_dashboard
WORKDIR /my_dashboard
ADD Gemfile /my_dashboard/Gemfile
ADD Gemfile.lock /my_dashboard/Gemfile.lock
RUN gem install bundler
RUN bundle install
ADD . /my_dashboard
CMD rackup --port=$PORT

In order to build this image, we’ll need to install Docker. Assuming we’re on Mac, we’ll head over here to install Docker. Once we’ve installed Docker, we’ll cd into the project directory and run the following commands:

docker build . -t my_dashboard
docker run -p 9292:9292 my_dashboard rackup

Now we should have the app up and running!

If everything looks good, deploy the image manually to Heroku using the following commands.

heroku plugins:install heroku-container-registry
heroku container:login
heroku container:push
heroku open

Our dashboard is alive on Docker!

Building and deploying via CI

We want to be able to deploy our app from our continuous integration system instead of pushing manually. We’ll use CircleCI.

Add a circle.yml file to our project with the following config.

machine:
  services:
    - docker

dependencies:
  override:
    - echo "no deps"

test:
  post:
    - echo "yay - no tests"

deployment:
  production:
    branch: master
    commands:
      - docker build -t my_dashboard .
      - docker login --email=_ --username=_ --password=$HEROKU_TOKEN registry.heroku.com
      - docker tag my_dashboard:latest registry.heroku.com/my_dashboard/web
      - docker push registry.heroku.com/my_dashboard/web

We’ll need to configure an environment variable for HEROKU_TOKEN (taken from our Heroku dashboard) so Circle can authenticate against the Heroku container registry.

Upon the next successful master build, we should see our image be built and then deployed to Heroku all from CI!

Open questions

Working with Docker raises some open questions around developer workflow, especially for more complicated applications (e.g. those with a separate worker process).

Here are some of the issues:

  • How do we run tests? Do we build an image (including test gems) and run tests against it?
  • How would we manage compiling assets if we needed to?
  • How do we ergonomically deploy multiple processes (assuming one process per container)?

Further reading

This post is just a taste of working with Docker. If you’re interested, check out the following resources to learn more.