Introducing 'heroku docker:release': Build & Deploy Heroku Apps with Docker

Important update

We've recently made some big updates to our support for Docker and the feature described in this blog post has been deprecated. Learn more in the container registry and runtime dev center documentation.


When Heroku launched the Cedar container stack 4 years ago, we became one of the first companies to use Linux Containers (LXC) to create a secure, performant and scalable cloud platform. Heroku has been a leader in the containerization movement, and we’ve spent years hardening, honing and evolving our runtime container stack. This means that developers can git push apps written in their favorite language and Heroku will build containers that are deployed to a production-quality environment. With this approach, developers are set free from managing operating systems, package updates, server infrastructure and their inevitably complex interactions.

Containers are essential to making this model work, as they create firm abstractions and boundaries between an application’s code and all the dependent pieces necessary to make them run. And the benefits of containers for deploying and running apps are familiar to most developers using Heroku; freedom from having to manage down-stack components, confidence that apps will continue to run as operating system and environment dependencies change, and the ability to start, stop and scale apps quickly.

As the container ecosystem has evolved, there’s an opportunity to bring the benefits of this technology not just to running apps on the server, but also building them on the desktop. In doing so, the hope is to address the challenges of creating and managing local development environments, as installing and managing local language runtimes, frameworks and associated dependencies is still a major time-suck for developers — problems that are made worse by the need for local environments to match production so that bugs can be identified and fixed before deploying.

Today, Heroku is releasing a beta version of heroku docker:release. This new CLI functionality leverages the increasing availability of Docker on the desktop, and combines the benefits of local container development with the proven Heroku Cedar container runtime. Using Docker and heroku docker:release, developers can run apps in containers similar to the Heroku runtime and get high fidelity dev/prod parity, whether they’re developing on OS X, Linux or Windows.

How to use

The new local dev experience ships as a Toolbelt plugin. Run the following command to install:

heroku plugins:install heroku-docker

The plugin requires a working Docker installation. We recommend boot2docker for users on OS X or Windows. The rest of this section assumes you’re on OS X. You can check your installation by running docker ps.

Let’s start by grabbing the Heroku Node.js getting started sample, and creating a Dockerfile for it:

$ git clone https://github.com/heroku/node-js-getting-started.git
...
$ cd node-js-getting-started
$ heroku docker:init
Wrote Dockerfile (node)

Your local Docker-based development environment is now initialized, and you can take a look at the Dockerfile written to the app directory. Notice that the Dockerfile derives from heroku/cedar-14, the Docker image for Heroku’s newest runtime stack. The rest of the Dockerfile pulls in the appropriate Node.js runtime for your app and sets up the container.

You can now run the app locally (you will probably see a different IP address):

$ heroku docker:start
...
web process will be available at http://192.168.59.103:3000/

Next, let’s install a new dependency for the app:

$ heroku docker:exec npm install --save --no-bin-links cool-ascii-faces

The npm install command is run inside an ephemeral Docker container that is torn down after the command completes. While npm install ran, the working directory was mounted inside the container, and node modules were installed for the app. You can verify this by running ls node_modules. Notice that no working Node or npm installation was required on your machine — these came courtesy of the Docker image created by Heroku. This also means that any node-modules with natively compiled binaries are built for Linux, and not for for the host operating system that you’re developing on.

If you want, you can also deploy the app to Heroku from out of the Docker image you just created:

$ heroku create
...
$ heroku docker:release
…
$ heroku open

docker:release doesn’t use Heroku’s traditional Buildpack-based build system. Instead, it creates a Heroku-compatible slug from your local Docker image, and deploys it directly to Heroku. If you prefer the standard approach of git-pushing to Heroku and having production builds happen with buildpacks, then that works fine too.

For details on how to use the plugin, see the Dev Center overview article.

How it works

The plugin consists of 3 components:

  • The heroku/cedar:14 Docker image published in the Docker registry (this is the image that powers the Heroku Cedar-14 stack)
  • Language specific Dockerfile templates that derive from the heroku/cedar:14 image (Node.js and Ruby currently supported, more to come)
  • CLI commands that invoke Docker to run apps in containers

These are the mechanics of the plugin:

  • heroku docker:init will try to detect what language and framework is used for your app and in what versions, and write an appropriate Dockerfile.
  • heroku docker:exec {command} builds image from Dockerfile (if one does not already exist) and runs it in a Docker container using docker run. It also mounts the working dir so that changes made by the command are persisted on your machine.
  • heroku docker:start {command} bundles your source code into a self-contained Docker image and runs Procfile commands. Changes made to the container file system while running are not reflected on your machine.
  • heroku docker:release starts the container and extracts the /app directory into a tarball that is deployed to Heroku using the Release API. The tarball includes both the language runtime and the source code for your app.

At any point in the flow above, you can break out and run Docker commands directly. For example:

  • docker images to list and manage the images created by the plugin
  • docker ps to list running containers
  • docker exec to run additional commands in a running container, eg. docker exec -ti {image-id} bash to get shell access to a running container

Advanced uses

When you run heroku docker:init, a Dockerfile is written to your app dir. You can modify this Dockerfile and rebuild images, for example to add binary dependencies to your container. See the Node.js guide for an example of how to bundle GraphicsMagick with a Node.js app and deploy it to Heroku. Note that you have to be careful to install extra stack components in the /app folder to get them deployed to Heroku. /app is the directory that’s mounted inside containers running on the platform.

You can also add your own language templates and we welcome contributions. Check out the plugin /platforms directory for inspiration and feel free to send pull request if you get more languages working.

The tar’ing approach from the release command is great for creating deployable slugs from scratch (i.e. not using buildpacks). If you’re currently creating slugs from scratch using OS X or Windows, using the plugin and heroku docker:release is a more dependable approach than trying to create Linux-compatible slugs from language binaries installed on those OS’s.

Finally, the Cedar-14-based Docker images created by the plugin are self-contained and fully portable. If you want, you can run them on your own servers or on other platforms that have Docker support.

See the Docker documentation on Dev Center for details.

Summary

Heroku has many years experience running containers securely and efficiently for our customers. We’re delighted that Docker is becoming a popular developer technology and for the promise this holds for bringing containers to a much greater audience.

Docker and its ecosystem is an exciting, but new, development for our industry. We are releasing this new feature as beta for adventurous users and there are still rough edges and bumps to be sanded down. As we evolve the Docker-based development workflow, we’re looking to ship more components of the Heroku platform as containers, for even better parity. We look forward to your help and feedback in extending containers to this new area.

Browse the blog archives, subscribe to the full-text feed, or visit the engineering blog.