Imagine you've got a new Macbook with an ARM-based processor (Apple M1), and you're trying to build a collection of services defined in a docker-compose.yml
file.
Each service definition has a build
property which points to a folder containing a Dockerfile
.
If you're lucky, you can run docker-compose build
, then docker-compose up
, and everything will be fine - it'll pull linux/arm64
base images, install arm64
packages using the package manager of the operating system in the container, build your image and run the service with no problems.
However, if there's a service which uses a base image that's only available for amd64
, or there are some old dependencies which are only available for amd64
, or the service won't run in an arm64
container for some reason, then you'll need to specify the target platform for this particular service.
To accomplish this, use Docker's new buildx
command that can build images for multiple architectures.
Firstly, tell Docker to use buildkit
, and tell docker-compose to use Docker's CLI instead of its own python Docker client:
export DOCKER_BUILDKIT=1
export COMPOSE_DOCKER_CLI_BUILD=1
You may also need to enable experimental Docker CLI features, by editing ~/.docker/config.json and setting experimental
to enabled
.
Next, define the platform to build for:
-
Most straightforwardly, if you want to force an image to always build for the same platform, add the platform to the
FROM
line of the Dockerfile, e.g.FROM --platform=linux/amd64 node:14
. -
If you want to build all images for a specific platform, set
DOCKER_DEFAULT_PLATFORM=linux/amd64
when running docker-compose. - To specify the platform for specific services in
docker-compose.yml
, set theplatform
property for each service, e.g.platform: linux/amd64
However, there's currently a bug which means theplatform
property doesn't get picked up (it should be fixed in the next release). There's also a bit of confusion about whetherplatform
belongs in the service definition or in the build definition. -
Use
docker buildx bake
to build the image for each service, and specify the platform for each service in a separate HCL file.
docker buildx bake
reads the build definitions from docker-compose.yml
, but there are some differences:
- Unlike docker-compose, it doesn't automatically read environment variables from
.env
, so you need to prefix the command withenv $(cat .env | grep -v "#" | xargs)
. - Unlike docker-compose, it doesn't read the
platform
property to define the target platform. In theory it could use abuild.platform
property, but this isn't part of the Compose spec yet. Instead, you need to define the target platform in a separate HCL file.
An example HCL file looks like this:
target "foo" {
platforms = ["linux/amd64"]
}
In summary, run env $(cat .env | grep -v "#" | xargs) docker buildx bake --load -f docker-compose.yml -f services.hcl
to build services for a mix of target platforms.