Skip Navigation
Show nav
Dev Center
  • Get Started
  • Documentation
  • Changelog
  • Search
  • Get Started
    • Node.js
    • Ruby on Rails
    • Ruby
    • Python
    • Java
    • PHP
    • Go
    • Scala
    • Clojure
    • .NET
  • Documentation
  • Changelog
  • More
    Additional Resources
    • Home
    • Elements
    • Products
    • Pricing
    • Careers
    • Help
    • Status
    • Events
    • Podcasts
    • Compliance Center
    Heroku Blog

    Heroku Blog

    Find out what's new with Heroku on our blog.

    Visit Blog
  • Log inorSign up
Hide categories

Categories

  • Heroku Architecture
    • Compute (Dynos)
      • Dyno Management
      • Dyno Concepts
      • Dyno Behavior
      • Dyno Reference
      • Dyno Troubleshooting
    • Stacks (operating system images)
    • Networking & DNS
    • Platform Policies
    • Platform Principles
  • Developer Tools
    • Command Line
    • Heroku VS Code Extension
  • Deployment
    • Deploying with Git
    • Deploying with Docker
    • Deployment Integrations
  • Continuous Delivery & Integration (Heroku Flow)
    • Continuous Integration
  • Language Support
    • Node.js
      • Working with Node.js
      • Troubleshooting Node.js Apps
      • Node.js Behavior in Heroku
    • Ruby
      • Rails Support
      • Working with Bundler
      • Working with Ruby
      • Ruby Behavior in Heroku
      • Troubleshooting Ruby Apps
    • Python
      • Working with Python
      • Background Jobs in Python
      • Python Behavior in Heroku
      • Working with Django
    • Java
      • Java Behavior in Heroku
      • Working with Java
      • Working with Maven
      • Working with Spring Boot
      • Troubleshooting Java Apps
    • PHP
      • PHP Behavior in Heroku
      • Working with PHP
    • Go
      • Go Dependency Management
    • Scala
    • Clojure
    • .NET
      • Working with .NET
  • Databases & Data Management
    • Heroku Postgres
      • Postgres Basics
      • Postgres Getting Started
      • Postgres Performance
      • Postgres Data Transfer & Preservation
      • Postgres Availability
      • Postgres Special Topics
      • Migrating to Heroku Postgres
    • Heroku Key-Value Store
    • Apache Kafka on Heroku
    • Other Data Stores
  • AI
    • Model Context Protocol
    • Vector Database
    • Working with AI
    • Heroku Inference
      • Inference API
      • Quick Start Guides
      • AI Models
      • Inference Essentials
  • Monitoring & Metrics
    • Logging
  • App Performance
  • Add-ons
    • All Add-ons
  • Collaboration
  • Security
    • App Security
    • Identities & Authentication
      • Single Sign-on (SSO)
    • Private Spaces
      • Infrastructure Networking
    • Compliance
  • Heroku Enterprise
    • Enterprise Accounts
    • Enterprise Teams
    • Heroku Connect (Salesforce sync)
      • Heroku Connect Administration
      • Heroku Connect Reference
      • Heroku Connect Troubleshooting
  • Patterns & Best Practices
  • Extending Heroku
    • Platform API
    • App Webhooks
    • Heroku Labs
    • Building Add-ons
      • Add-on Development Tasks
      • Add-on APIs
      • Add-on Guidelines & Requirements
    • Building CLI Plugins
    • Developing Buildpacks
    • Dev Center
  • Accounts & Billing
  • Troubleshooting & Support
  • Integrating with Salesforce
  • Deployment
  • Deploying with Docker
  • Container Registry & Runtime (Docker Deploys)

Container Registry & Runtime (Docker Deploys)

English — 日本語に切り替える

Last updated December 03, 2024

Table of Contents

  • Getting started
  • Logging in to the registry
  • Building and pushing image(s)
  • Releasing an image
  • One-off dynos
  • Using a CI/CD platform
  • Release phase
  • Dockerfile commands and runtime
  • Testing an image locally
  • Heroku base images
  • Changing deployment method
  • Known issues and limitations

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.

The Heroku container stack is intended for advanced use-cases only. Unless you have a specific need for custom Docker images, we recommend using Heroku’s default buildpack-powered build system instead, which offers automatic base image security updates, language-specific optimisations, and avoids the need to maintain a Dockerfile.

 

This article is only applicable to Cedar-generation apps.

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:

$ cd alpinehelloworld
$ heroku create --stack container
Creating salty-fortress-4191... done, stack is container
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)

Make sure your app’s stack is set to container via heroku stack:set container

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:

  • Bamboo
  • Codefresh
  • Codeship
  • CircleCI
  • Jenkins
  • Semaphore CI
  • TravisCI

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 only be available in your application logs.

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. EXPOSE in Dockerfile is 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 WORKDIR.
  • ENV, for setting environment variables, is supported.
    • We suggest using ENV for runtime variables (e.g., GEM_PATH) and heroku config for credentials, so that sensitive credentials aren’t accidentally checked into source code control.
  • ENTRYPOINT is optional. If not set, /bin/sh -c will be used
    • CMD will 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 use ENTRYPOINT

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 - While EXPOSE can 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. STOPSIGNAL is not respected.
  • SHELL - The default shell for Docker images is /bin/sh, you can override with ENTRYPOINT if necessary.
  • HEALTHCHECK - While HEALTHCHECK is 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.

Heroku base images

The Heroku stacks are available as Docker base images for convenience (see each stack’s detail page for Docker image names and tags). However for the fastest build and boot times we strongly recommend that you instead use a smaller less-general-purpose base image, such as one of the official Docker images for your app’s language.

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.

If you no longer wish to use Docker images for your app, and would like to switch back to buildpack-based deploys using the Heroku curated stack, use the heroku stack:set command to switch to one of the available heroku-* stacks. See migrating to a different stack for more details.

Known issues and limitations

  • Apps using the container stack must be rebuilt/redeployed in order to pick up any security updates and bug fixes to the underlying base Docker image. In contrast Heroku buildpack/slug based deployments receive automatic updates to the underlying stack’s base image, without the need to rebuild/redeploy the app.
  • Heroku only supports x86_64 images. An unsupported architecture error returns if you try to upload images built to run on other architectures. You also get an Exec format error if you try to use other architectures, like ARM64, with the Container Runtime. You must ensure your container builder outputs x86_64 images. To ensure the built image is built for Heroku correctly, pass the platform flag to the Docker build command: docker build --platform linux/amd64.
  • Review apps are not supported when building Docker images locally and then pushing them to the Heroku Container Registry. To use Docker with Review Apps, you must instead 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. As such, images with more than 40 layers may fail to start in the Common Runtime. See the best practices for writing Dockerfiles guide for suggestions on how to reduce Docker image size.
  • The Dockerfile commands listed here are not supported.
  • Container apps in Private or Shield spaces do not run .profile or .profile.d/* scripts when booting a dyno.
  • Whiteout files (created when files from an earlier Docker image layer are deleted in a later layer) are not honoured due to the way Docker images are extracted at runtime. On the Common Runtime only, it’s possible to switch to a newer extraction method that resolves this, via heroku labs:enable runtime-new-layer-extract. There is currently no workaround for Private Spaces.
  • The --exit-code argument to heroku run is not supported, unless a workaround is applied.
  • If an existing app is migrated to the container stack and the app’s process names are changed at the same time as the migration, the dynos for the old processes may persist until they are cleaned up manually. To avoid this, either change the process names before/after the container stack migration (rather than as part of the migration), or else scale down the old processes during the migration.
  • If apps using the container stack are built using Heroku’s build system (rather than locally), then the known issues and limitations of heroku.yml container builds also apply.
  • The Heroku Container Registry only supports Docker Image Manifest V2, Schema 2 (application/vnd.docker.distribution.manifest.v2+json). It doesn’t support other manifest types, such as OCI Image Manifest (application/vnd.oci.image.manifest.v1+json).

Keep reading

  • Deploying with Docker

Feedback

Log in to submit feedback.

Local Development with Docker Compose Local Development with Docker Compose

Information & Support

  • Getting Started
  • Documentation
  • Changelog
  • Compliance Center
  • Training & Education
  • Blog
  • Support Channels
  • Status

Language Reference

  • Node.js
  • Ruby
  • Java
  • PHP
  • Python
  • Go
  • Scala
  • Clojure
  • .NET

Other Resources

  • Careers
  • Elements
  • Products
  • Pricing
  • RSS
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku Blog
    • Heroku News Blog
    • Heroku Engineering Blog
  • Twitter
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku
    • Heroku Status
  • Github
  • LinkedIn
  • © 2025 Salesforce, Inc. All rights reserved. Various trademarks held by their respective owners. Salesforce Tower, 415 Mission Street, 3rd Floor, San Francisco, CA 94105, United States
  • heroku.com
  • Legal
  • Terms of Service
  • Privacy Information
  • Responsible Disclosure
  • Trust
  • Contact
  • Cookie Preferences
  • Your Privacy Choices