Building Docker Images with heroku.yml
Last updated November 02, 2022
Table of Contents
heroku.yml file is a manifest you can use to define your Heroku app. It allows you to:
- Build Docker images on Heroku
- Specify add-ons and config vars to create during app provisioning
- Take advantage of Review Apps when deploying Docker-based applications
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 stack-image security updates, language-specific optimisations, and avoids the need to maintain a
heroku.ymlfile in your application’s root directory. The following example
heroku.ymlspecifies the Docker image to build for the app’s
build: docker: web: Dockerfile run: web: bundle exec puma -C config/puma.rb
In this example, both
Dockerfileare in the same directory. If the
Dockerfilelives in a non-root directory, specify the relative path in the
build.docker.webvalue, such as
If you don’t include a
runsection, Heroku uses the
CMDspecified in the
Commit the file to your repo:
$ git add heroku.yml $ git commit -m "Add heroku.yml"
Set the stack of your app to
$ heroku stack:set container
Push your app to Heroku:
$ git push heroku master
Your application will be built, and Heroku will use the
run command provided in
heroku.yml instead of your
heroku.yml manifest has 4 top-level sections:
setup- Specifies the add-ons and config vars to create during app provisioning
build- Specifies the
release- Specifies the release phase tasks to execute
run- Specifies process types and the commands to run for each
Details for each of these sections are described below.
Here’s an example that illustrates using a
heroku.yml manifest to build Docker images:
setup: addons: - plan: heroku-postgresql as: DATABASE config: S3_BUCKET: my-example-bucket build: docker: web: Dockerfile worker: worker/Dockerfile config: RAILS_ENV: development FOO: bar release: command: - ./deployment-tasks.sh image: worker run: web: bundle exec puma -C config/puma.rb worker: python myworker.py asset-syncer: command: - python asset-syncer.py image: worker
setup: Defining your app’s environment
heroku.yml supports creating add-ons at app provisioning time. To provision an add-on, add it to the
setup: addons: - plan: heroku-postgresql as: DATABASE
as option allows you to attach multiple instances of the same add-on provider with different names.
Setting config vars
heroku.yml supports setting config vars at app provisioning time. To set a config var, add it to the
setup: config: S3_BUCKET: my-example-bucket
build: Defining your build
Dockerfiles using paths relative to
build: docker: web: Dockerfile worker: worker/Dockerfile
The Docker build context is set to the directory containing the Dockerfile.
If you do not specify a
run section, the
CMD specified in the Dockerfile is used.
Setting build-time environment variables
config field of the
build section allows you set environment variables available to the build environment. Variables set in this section do not create runtime config vars. Also runtime config vars (e.g., those set with
heroku config:set) are not available at build-time.
build: config: RAILS_ENV: development FOO: bar
Each build-time environment variable must match an
ARG line in your
ARG RAILS_ENV=production ARG FOO
Targeting a stage from a multi-stage build
With a multi-stage Docker build you can manage build and production images for your application in a single
Dockerfile. For example, you might want to have specific packages or database migration scripts only available at build and release time, but not in your final production image.
Here’s an example of a multi-stage
builder stage includes database migration scripts and the
production stage only has the app code and dependencies necessary to run the app.
FROM heroku/heroku:18-build AS builder ... FROM heroku/heroku:18 AS production ...
Here’s an example
heroku.yml which specifies to only use the output of the
builder stage as the
build: docker: release: dockerfile: Dockerfile target: builder web: Dockerfile
release: Configuring release phase
Release phase enables you to run tasks before a new release is deployed to production (e.g., sending CSS/JS/assets to a CDN, priming cache stores, or running database schema migrations).
To define a release phase command, specify a
release section with a
command in your
heroku.yml manifest, as well as the
image you would like to use:
build: docker: web: Dockerfile worker: worker/Dockerfile release: image: worker command: - ./deployment-tasks.sh
Runtime config vars, such as a database connection url, are available during release phase.
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 are available in your application logs. If you’re using a Heroku stack as your base image,
curl is included.
run: Defining the processes to run
run section allows you to define the process that you would like Heroku to run when your application starts. If your project also includes a
Procfile, it is ignored and
run is used instead.
run: web: bundle exec puma -C config/puma.rb
To use a specific Docker image for multiple processes, specify it with
build: docker: web: Dockerfile run: web: bundle exec puma -C config/puma.rb worker: command: - python myworker.py image: web
If you do not include a
run section in your
heroku.yml manifest, the
CMD is used instead.
To learn more about the runtime requirements for Docker images, review the Container Registry and Runtime documentation.
Review apps and app.json
Apps using Docker images can’t use
pr-predestroy scripts. These scripts get ignored if included in your
Creating your app from ‘setup’
Creating an app from
setup is currently in beta. Please email firstname.lastname@example.org to provide feedback.
To create an app from the
setup section defined in your
heroku.yml manifest, install the
heroku-manifest plugin from the
beta update channel:
$ heroku update beta $ heroku plugins:install @heroku-cli/plugin-manifest
You can switch back to the stable update stream and remove the plugin at any time:
$ heroku update stable $ heroku plugins:remove manifest
Then create your app using the
--manifest flag. The stack of the app will automatically be set to
$ heroku create your-app-name --manifest Creating ⬢ your-app-name... done, stack is container Adding heroku-postgresql... done Setting config vars... done
heroku.yml to git:
$ git add heroku.yml $ git commit -m "Added heroku.yml"
Push the code:
$ git push heroku master
Known issues and limitations
- Caching of Docker image layers is not supported.
- Private Spaces do not honor the
heroku.ymlyet, so the command must instead be specified via the Dockerfile.
- Private repositories/registries are not supported in the
FROMline of the Dockerfile.
- The Docker build context is always set to the directory containing the
Dockerfileand cannot be configured independently.
- The environment where
heroku.ymlbuilds occur may use a slightly different CPU generation to that at runtime (depending on choice of runtime environment and dyno size), such that binary executables compiled for a specific CPU instruction set may not work at runtime. If you encounter
Illegal instructionerrors at runtime, try adjusting the build time compilation options to disable CPU-specific optimisations. When using GCC, this can be achieved by passing
-march=x86-64 -mtune=genericto the compiler.
- The known issues and limitations of the underlying container runtime also apply.