Building Docker Images with heroku.yml
Last updated November 02, 2022
Table of Contents
The 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
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 stack-image security updates, language-specific optimisations, and avoids the need to maintain a Dockerfile
.
Getting started
Create a
heroku.yml
file in your application’s root directory. The following exampleheroku.yml
specifies the Docker image to build for the app’sweb
process:build: docker: web: Dockerfile run: web: bundle exec puma -C config/puma.rb
In this example, both
heroku.yml
andDockerfile
are in the same directory. If theDockerfile
lives in a non-root directory, specify the relative path in thebuild.docker.web
value, such asapp/Dockerfile
.If you don’t include a
run
section, Heroku uses theCMD
specified in theDockerfile
.Commit the file to your repo:
$ git add heroku.yml $ git commit -m "Add heroku.yml"
Set the stack of your app to
container
:$ 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 Procfile
.
heroku.yml Overview
A heroku.yml
manifest has 4 top-level sections:
setup
- Specifies the add-ons and config vars to create during app provisioningbuild
- Specifies theDockerfile
to buildrelease
- Specifies the release phase tasks to executerun
- 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
Configuring add-ons
heroku.yml
supports creating add-ons at app provisioning time. To provision an add-on, add it to the setup.addons
section:
setup:
addons:
- plan: heroku-postgresql
as: DATABASE
The optional 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
section:
setup:
config:
S3_BUCKET: my-example-bucket
Learn how to create an app from setup
.
build: Defining your build
Specify Dockerfile
s using paths relative to heroku.yml
:
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
The 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 Dockerfile
:
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 Dockerfile
. The 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 release
image:
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
The 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 image
:
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 Dockerfile
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
An app.json
file is still required when using Review Apps with the heroku.yml
manifest. Be sure to set the stack
value in your app.json
file to container
.
Apps using Docker images can’t use pr-predestroy
scripts. These scripts get ignored if included in your app.json
file.
Creating your app from ‘setup’
Creating an app from setup
is currently in beta. Please email heroku-build-manifest-feedback@salesforce.com 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 container
:
$ heroku create your-app-name --manifest
Creating ⬢ your-app-name... done, stack is container
Adding heroku-postgresql... done
Setting config vars... done
Commit 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
run
section inheroku.yml
yet, so the command must instead be specified via the Dockerfile. - Private repositories/registries are not supported in the
FROM
line of the Dockerfile. - The Docker build context is always set to the directory containing the
Dockerfile
and cannot be configured independently. - The environment where
heroku.yml
builds 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 encounterIllegal instruction
errors 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=generic
to the compiler. - The known issues and limitations of the underlying container runtime also apply.