Building Docker Images with heroku.yml
Last updated December 03, 2024
Table of Contents
The heroku.yml
file is a manifest used 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. It
offers automatic base image security updates and language-specific optimizations. It also avoids the need to maintain a Dockerfile
.
This article is only applicable to Cedar-generation apps.
Get Started
Create a
heroku.yml
file in your application’s root directory. This 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,
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 your app’s stack to
container
.$ heroku stack:set container
Push your app to Heroku.
$ git push heroku main
After building your application, Heroku uses the run
command provided in heroku.yml
instead of your Procfile
.
heroku.yml Overview
A heroku.yml
manifest has four 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 type
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: Define Your App’s Environment
Configure 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
With the as
option, you can attach multiple instances of the same add-on provider with different names.
Set 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
Build: Define Your Build
Specify Dockerfile
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 don’t specify a run
section, the CMD
specified in the Dockerfile
is used.
Set Build Time Environment Variables
With the config
field of the build
section, you can set environment variables available to the build environment. Variables set in this section don’t create runtime config vars. Also runtime config vars, such as those config vars set with heroku config:set
, aren’t 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
Target 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 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 required to run the app.
FROM heroku/heroku:22-build AS builder
...
FROM heroku/heroku:22 AS production
...
Here’s an example heroku.yml
that specifies only using the output of the builder
stage as the release
image.
build:
docker:
release:
dockerfile: Dockerfile
target: builder
web: Dockerfile
Release: Configure Release Phase
With release phase, you can run tasks before a new release deploys to production. For example, 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 and the image
that you want 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 the release phase.
If you want to see streaming logs as the release phase executes, your Docker image must have curl
. If your Docker image doesn’t 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: Define the Processes to Run
In the run
section, you can define the process that you want Heroku to run when your application starts. If your project also includes a Procfile
, it’s ignored and usesrun
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 don’t include a run
section in your heroku.yml
manifest, the Dockerfile
CMD
is used.
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.
Known Issues and Limitations
- Caching of Docker image layers isn’t supported.
- Private Spaces don’t honor the
run
section inheroku.yml
. You must specify the command via the Dockerfile. - Private repositories/registries aren’t supported in the
FROM
line of the Dockerfile. - The Docker build context is always set to the directory containing the
Dockerfile
and can’t be configured independently. - The environment where
heroku.yml
builds occur can use a slightly different CPU generation at runtime, depending on the choice of runtime environment and dyno size. As a result, it’s possible that binary executables compiled for a specific CPU instruction set don’t work at runtime. If you encounterIllegal instruction
errors at runtime, try adjusting the build time compilation options to disable CPU-specific optimizations. When using GCC, disable them by passing-march=x86-64 -mtune=generic
to the compiler. - The known issues and limitations of the underlying container runtime also apply.