Container Registry & Runtime (Docker Deploys)
Last updated 06 December 2017
Table of Contents
Make sure you have a working Docker installation (eg.
docker ps) and that you’re logged in to Heroku (
Log in to Container Registry:
$ heroku container:login
Get sample code by cloning an Alpine-based python example:
$ git clone https://github.com/heroku/alpinehelloworld.git
Navigate to the app’s directory and create a Heroku app:
$ heroku create Creating salty-fortress-4191... done, stack is cedar-14 https://salty-fortress-4191.herokuapp.com/ | https://git.heroku.com/salty-fortress-4191.git
Build the image and push to Container Registry:
$ heroku container:push web
Now open the app in your browser:
$ heroku open
Logging in to the registry
Heroku runs a container registry on
If you are using the Heroku CLI, you can log in with:
$ heroku container:login
or directly via the Docker CLI:
$ docker login --username=_ --password=$(heroku auth:token) registry.heroku.com
Pushing an image(s)
Build an image and push
To build an image and push it to Container Registry, make sure that your directory contains a Dockerfile. Then run:
$ heroku container:push <process-type>
Even if a process type is specified, a push to the registry will restart all processes. For example, if you specify
worker processes will also be restarted.
Pushing an existing image
To push an image to Heroku, such as one pulled from Docker Hub, tag it and push it according to this naming template:
$ docker tag <image> registry.heroku.com/<app>/<process-type> $ docker push registry.heroku.com/<app>/<process-type>
After the image has successfully been pushed, open your app:
$ heroku open -a <app>
Pushing multiple images
To push multiple images, rename your Dockerfiles using
$ ls -R ./webapp: Dockerfile.web ./worker: Dockerfile.worker ./image-processor: Dockerfile.image
Then, from the root directory of the project, run:
$ heroku container:push --recursive === Building web === Building worker === Building image === Pushing web === Pushing worker === Pushing image
This will build and push all 3 images. If you only want to push specific images, you can specify the process types:
$ heroku container:push web worker --recursive === Building web === Building worker === Pushing web === Pushing worker
If your app is composed of multiple Docker images, you can target the process type when creating a one-off dyno:
$ heroku run bash --type=worker Running bash on ⬢ multidockerfile... up, worker.5180 $
If the type is not specified, the
web image is used.
Using a CI/CD platform
If you are using a third party CI/CD platform, you can push images to the registry. First authenticate with the following information:
- Registry URL:
your Heroku email address
your Heroku email address
your Heroku API key
Many CI/CD providers have documentation about how to build and push images to a Docker registry:
Dockerfile commands and runtime
When you push Docker images to Container Registry for an app, those images are immediately released to the Heroku app. Docker images are the functional equivalent of slugs built with buildpacks. Docker images run in dynos the same way that slugs do, and under the same constraints:
- The web process must listen for HTTP traffic on
$PORT, which is set by Heroku.
Dockerfileis not respected, but can be used for local testing. Only HTTP requests are supported.
- Network linking of dynos is not supported.
- The filesystem is ephemeral.
- The working directory is
/. You can set a different directory using
ENV, for setting environment variables, is supported.
- We suggest using
ENVfor runtime variables (e.g.,
heroku configfor credentials, so that sensitive credentials aren’t accidentally checked into source code control.
- We suggest using
ENTRYPOINTis optional. If not set,
/bin/sh -cwill be used
CMDis required. If
CMDis missing, the registry will return an error
We strongly recommend testing images locally as a non-root user, as containers are not run with root privileges on Heroku.
Unsupported Dockerfile commands
VOLUME- Volume mounting is not supported. The filesystem of the dyno is ephemeral.
EXPOSEcan be used for local testing, it is not supported in Heroku’s container runtime. Instead your web process/code should get the $PORT environment variable.
STOPSIGNAL- The dyno manager will request that your processes shut down gracefully by sending them a SIGTERM signal followed by a SIGKILL signal.
STOPSIGNALis not respected.
SHELL- The default shell for Docker images is
/bin/sh, you can override with
HEALTHCHECKis not currently supported, the Heroku Dyno manager automatically checks the health of your running containers.
Testing an image locally
When testing an image locally there are a number of best practices. These best practices are implemented in this example Dockerfile.
Run the image as a non-root user
We strongly recommend testing images locally as a non-root user, as containers are not run with root privileges in Heroku. Immediately before
CMD you can add the following commands to your Dockerfile:
If using Alpine:
RUN adduser -D myuser USER myuser
If using Ubuntu:
RUN useradd -m myuser USER myuser
To confirm that your container is running as a non-root user, attach to a running container and then run the
$ docker exec <container-id> bash $ whoami myuser
When deployed to Heroku, we also run your container as a non-root user (although we do not use the
USER specified in the Dockerfile).
$ heroku run bash $ whoami U7729
Get the port from the environment variable
For testing purposes, we suggest that your
Dockerfile or code read from the $PORT environment variable, for example:
CMD gunicorn --bind 0.0.0.0:$PORT wsgi
When running a Docker container locally, you can set an environment variable using the -e flag:
$ docker run -p 5000:5000 -e PORT=5000 <image-name>
Setting multiple environment variables
When you use heroku locally, you can set config vars in a .env file. When
heroku local is run .env is read and each name/value pair is set in the environment. You can use this same .env file when using Docker:
$ docker run -p 5000:5000 --env-file .env <image-name>
We suggest adding the .env file to your .dockerignore file.
Take advantage of Docker Compose for multi-container applications
If you’ve created a multi-container application you can use Docker Compose to define your local development environment. Learn how to use Docker Compose for local development.
- More information on running a Docker image locally is available in Docker’s official documentation.
- Learn more about using Docker Compose for local development.
Both Cedar-14 and Heroku-16 are available as Docker images. However, you are free to use any base image you want – using a Heroku stack image is not required. If you chose to use a Heroku image we suggest Heroku-16 (465.3 MB), given it is much smaller than Cedar-14.
To use Heroku-16 as the base image in your
Getting the latest image
If you’ve already deployed your app with Heroku-16 as your base image, and want to get the latest version (which usually includes security updates), simply run:
$ docker pull heroku/heroku:16
and redeploy your application.
Changing deployment method
Once you deploy your application via Container Registry, the stack is set to
container. This means that your application is no longer using a Heroku-curated stack, but instead your own custom container. Once set to
container pushing your app via git is disabled. If you no longer wish to deploy your app via Container Registry, and instead want to use git, run
heroku stack:set heroku-16.
Known issues and limitations
- Review apps are not supported. To use Docker with review apps, try the new heroku.yml build manifest, which allows you to build Docker images on Heroku.
- While Docker images are not subject to size restrictions (unlike slugs), they are subject to the dyno boot time restriction. As layer count/image size grows, so will dyno boot time.
- Images with more than 40 layers may fail to start in the Common Runtime
- Pipeline promotions are not supported.
- Release phase is not supported.
- The commands listed here are not supported.