Container Registry & Runtime (Docker Deploys)
Last updated 06 August 2020
Table of Contents
Heroku Container Registry allows you to deploy your Docker images to Heroku. Both Common Runtime and Private Spaces are supported.
If you would like Heroku to build your Docker images, as well as take advantage of Review Apps, check out building Docker images with heroku.yml.
Getting started
Make sure you have a working Docker installation (eg. docker ps) and that you’re logged in to Heroku (heroku login).
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 heroku-18
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
Then release the image to your app:
$ heroku container:release web
Now open the app in your browser:
$ heroku open
Logging in to the registry
Heroku runs a container registry on registry.heroku.com.
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
Building and pushing 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 and run:
$ heroku container:push <process-type>
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>
By specifying the process type in the tag, you can release the image using the CLI. If you would prefer to not specify the process type in the tag, you’ll have to release via the API which uses the image_id.
Pushing multiple images
To push multiple images, rename your Dockerfiles using Dockerfile.<process-type>:
$ 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
Releasing an image
CLI
After you’ve successfully pushed an image to Container Registry, you can create a new release using:
$ heroku container:release web
If you have multiple images, list them:
$ heroku container:release web worker
In an app with multiple process types, if you only release one process type (e.g., heroku container:release web), all process types will be restarted.
API
curl --netrc -X PATCH https://api.heroku.com/apps/$APP_ID_OR_NAME/formation \
-d '{
"updates": [
{
"type": "web",
"docker_image": "$WEB_DOCKER_IMAGE_ID"
},
{
"type": "worker",
"docker_image": "$WORKER_DOCKER_IMAGE_ID"
}
]
}' \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.heroku+json; version=3.docker-releases"
For the curl --netrc option to work, you must have previously run heroku login to populate the API token in your .netrc file.
Getting a Docker image ID
The docker image values used when releasing an image via the platform API need to be in the format algorithm:hex. For example:
sha256:4d2647aab0e8fbe92cb0fc88c500eb51661c5907f4f14e79efe8bfbda1f7d159
To get this ID for your image you can run the following command:
$ docker inspect my_image --format={{.Id}}
sha256:4d2647aab0e8fbe92cb0fc88c500eb51661c5907f4f14e79efe8bfbda1f7d159
One-off dynos
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, theweb image is used.
Using a CI/CD platform
Currently, it is not possible to use Heroku CI to test container builds.
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:
registry.heroku.com - Username:
your Heroku email address - Email:
your Heroku email address - Password:
your Heroku API key
Many CI/CD providers have documentation about how to build and push images to a Docker registry:
Release phase
To use release phase push a Docker image named release:
$ heroku container:push release
When you release your Docker images, by running heroku container:release, your release phase process type needs to be specified:
$ heroku container:release web release
Releasing images web,release to your-app-name... done
Running release command...
Migrating database.
If you would like to see streaming logs as release phase executes, your Docker image is required to have curl. If your Docker image does not include curl, release phase logs will be available in your application logs. If you’re using Heroku-16 or Heroku-18 as your base image, curl is included.
Dockerfile commands and runtime
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.EXPOSEinDockerfileis 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 usingWORKDIR. -
ENV, for setting environment variables, is supported.- We suggest using
ENVfor runtime variables (e.g.,GEM_PATH) andheroku 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-
CMDwill always be executed by a shell so that config vars are made available to your process; to execute single binaries or use images without a shell please useENTRYPOINT
-
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. -
EXPOSE- WhileEXPOSEcan 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 withENTRYPOINTif necessary. -
HEALTHCHECK- WhileHEALTHCHECKis 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 whoami command:
$ 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.
Learn more
- More information on running a Docker image locally is available in Docker’s official documentation.
- Learn more about using Docker Compose for local development.
- See the best practices for writing Dockerfiles guide for tips on how to optimize the build performance and size of your Docker images.
Stack images
The Heroku-16 and Heroku-18 stacks are available as Docker images for convenience - however you are free to use any base image you want – using a Heroku stack image is not required. For the fastest build and boot times, we recommend using a smaller less-general-purpose base image, such as one of the official Docker images for your app’s language. If you do chose to use a Heroku image we suggest Heroku-18 (486MB).
To use Heroku-18 as the base image in your Dockerfile:
FROM heroku/heroku:18
Getting the latest image
If you’ve already deployed your app with Heroku-18 as your base image, and want to get the latest version (which usually includes security updates), simply run:
$ docker pull heroku/heroku:18
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-18.
Known issues and limitations
- Review apps are not supported. To use Docker with review apps, you can define your app with a heroku.yml manifest, which allows you to build Docker images on Heroku.
- Pipeline promotions are not supported.
- 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. See the best practices for writing Dockerfiles guide for suggestions on how to reduce Docker image size.
- Images with more than 40 layers may fail to start in the Common Runtime
- The commands listed here are not supported.
- Container apps in Private or Shield spaces do not run
.profileor.profile.d/*scripts when booting a dyno.