|
| 1 | +--- |
| 2 | +title: Building Compose projects with Bake |
| 3 | +description: Learn how to build Docker Compose projects with Docker Buildx Bake |
| 4 | +summary: | |
| 5 | + This guide demonstrates how you can use Bake to build production-grade images for Docker Compose projects. |
| 6 | +languages: [] |
| 7 | +tags: [devops] |
| 8 | +params: |
| 9 | + time: 20 minutes |
| 10 | +--- |
| 11 | + |
| 12 | +This guide explores how you can use Bake to build images for Docker Compose |
| 13 | +projects with multiple services. |
| 14 | + |
| 15 | +[Docker Buildx Bake](/manuals/build/bake/_index.md) is a build orchestration |
| 16 | +tool that enables declarative configuration for your builds, much like Docker |
| 17 | +Compose does for defining runtime stacks. For projects where Docker Compose is |
| 18 | +used to spin up services for local development, Bake offers a way of seamlessly |
| 19 | +extending the project with a production-ready build configuration. |
| 20 | + |
| 21 | +## Prerequisites |
| 22 | + |
| 23 | +This guide assumes that you're familiar with |
| 24 | + |
| 25 | +- Docker Compose |
| 26 | +- [Multi-stage builds](/manuals/build/building/multi-stage.md) |
| 27 | +- [Multi-platform builds](/manuals/build/building/multi-platform.md) |
| 28 | + |
| 29 | +## Orientation |
| 30 | + |
| 31 | +This guide will use the |
| 32 | +[dvdksn/example-voting-app](https://github.com/dvdksn/example-voting-app) |
| 33 | +repository as an example of a monorepo using Docker Compose that can be |
| 34 | +extended with Bake. |
| 35 | + |
| 36 | +```console |
| 37 | +$ git clone https://github.com/dvdksn/example-voting-app.git |
| 38 | +$ cd example-voting-app |
| 39 | +``` |
| 40 | + |
| 41 | +This repository uses Docker Compose to define the runtime configurations for |
| 42 | +running the application, in the `compose.yaml` file. This app consists of the |
| 43 | +following services: |
| 44 | + |
| 45 | +| Service | Description | |
| 46 | +| -------- | ---------------------------------------------------------------------- | |
| 47 | +| `vote` | A front-end web app in Python which lets you vote between two options. | |
| 48 | +| `result` | A Node.js web app which shows the results of the voting in real time. | |
| 49 | +| `worker` | A .NET worker which consumes votes and stores them in the database. | |
| 50 | +| `db` | A Postgres database backed by a Docker volume. | |
| 51 | +| `redis` | A Redis instance which collects new votes. | |
| 52 | +| `seed` | A utility container that seeds the database with mock data. | |
| 53 | + |
| 54 | +The `vote`, `result`, and `worker` services are built from code in this |
| 55 | +repository, whereas `db` and `redis` use pre-existing images Postgres and Redis |
| 56 | +images from Docker Hub. The `seed` service is a utility that invokes requests |
| 57 | +against the front-end service to populate the database, for testing purposes. |
| 58 | + |
| 59 | +## Build with Compose |
| 60 | + |
| 61 | +When you spin up a Docker Compose project, any services that define the `build` |
| 62 | +property is automatically built before the service is started. Here's the build |
| 63 | +configuration for the `vote` service in the example repository: |
| 64 | + |
| 65 | +```yaml {title="compose.yaml"} |
| 66 | +services: |
| 67 | + vote: |
| 68 | + build: |
| 69 | + context: ./vote # Build context |
| 70 | + target: dev # Dockerfile stage |
| 71 | +``` |
| 72 | +
|
| 73 | +The `vote`, `result`, and `worker` services all have a build configuration |
| 74 | +specified. Running `docker compose up` will trigger a build of these services. |
| 75 | + |
| 76 | +Did you know that you can also use Compose just to build the service images? |
| 77 | +The `docker compose build` command lets you invoke a build using the build |
| 78 | +configuration specified in the Compose file. For example, to build the `vote` |
| 79 | +service with this configuration, run: |
| 80 | + |
| 81 | +```console |
| 82 | +$ docker compose build vote |
| 83 | +``` |
| 84 | + |
| 85 | +Omit the service name to build all services at once: |
| 86 | + |
| 87 | +```console |
| 88 | +$ docker compose build |
| 89 | +``` |
| 90 | + |
| 91 | +The `docker compose build` command is useful when you only need to build images |
| 92 | +without running services. |
| 93 | + |
| 94 | +The Compose file format supports a number of properties for defining your |
| 95 | +build's configuration. For example, to specify the tag name for the images, set |
| 96 | +the `image` property on the service. |
| 97 | + |
| 98 | +```yaml |
| 99 | +services: |
| 100 | + vote: |
| 101 | + image: username/vote |
| 102 | + build: |
| 103 | + context: ./vote |
| 104 | + target: dev |
| 105 | + #... |
| 106 | +
|
| 107 | + result: |
| 108 | + image: username/result |
| 109 | + build: |
| 110 | + context: ./result |
| 111 | + #... |
| 112 | +
|
| 113 | + worker: |
| 114 | + image: username/worker |
| 115 | + build: |
| 116 | + context: ./worker |
| 117 | + #... |
| 118 | +``` |
| 119 | + |
| 120 | +Running `docker compose build` creates three service images with fully |
| 121 | +qualified image names that you can push to Docker Hub. |
| 122 | + |
| 123 | +The `build` property supports a [wide range](/reference/compose-file/build.md) |
| 124 | +of options for configuring builds. However, building production-grade images |
| 125 | +are often different from images used in local development. To avoid cluttering |
| 126 | +your Compose file with build configurations that might not be desirable for |
| 127 | +local builds, consider separating the production builds from the local builds |
| 128 | +by using Bake to build images for release. This approach separates concerns: |
| 129 | +using Compose for local development and Bake for production-ready builds, while |
| 130 | +still reusing service definitions and fundamental build configurations. |
| 131 | + |
| 132 | +## Build with Bake |
| 133 | + |
| 134 | +Like Compose, Bake parses the build definition for a project from a |
| 135 | +configuration file. Bake supports HashiCorp Configuration Language (HCL), JSON, |
| 136 | +and the Docker Compose YAML format. When you use Bake with multiple files, it |
| 137 | +will find and merge all of the applicable configuration files into one unified |
| 138 | +build configuration. The build options defined in your Compose file are |
| 139 | +extended, or in some cases overridden, by options specified in the Bake file. |
| 140 | + |
| 141 | +The following section explores how you can use Bake to extend the build options |
| 142 | +defined in your Compose file for production. |
| 143 | + |
| 144 | +### View the build configuration |
| 145 | + |
| 146 | +Bake automatically creates a build configuration from the `build` properties of |
| 147 | +your services. Use the `--print` flag for Bake to view the build configuration |
| 148 | +for a given Compose file. This flag evaluates the build configuration and |
| 149 | +outputs the build definition in JSON format. |
| 150 | + |
| 151 | +```console |
| 152 | +$ docker buildx bake --print |
| 153 | +``` |
| 154 | + |
| 155 | +The JSON-formatted output shows the group that would be executed, and all the |
| 156 | +targets of that group. A group is a collection of builds, and a target |
| 157 | +represents a single build. |
| 158 | + |
| 159 | +```json |
| 160 | +{ |
| 161 | + "group": { |
| 162 | + "default": { |
| 163 | + "targets": ["seed", "vote", "result", "worker"] |
| 164 | + } |
| 165 | + }, |
| 166 | + "target": { |
| 167 | + "result": { |
| 168 | + "context": "result", |
| 169 | + "dockerfile": "Dockerfile", |
| 170 | + "tags": ["username/result"] |
| 171 | + }, |
| 172 | + "seed": { |
| 173 | + "context": "seed-data", |
| 174 | + "dockerfile": "Dockerfile" |
| 175 | + }, |
| 176 | + "vote": { |
| 177 | + "context": "vote", |
| 178 | + "dockerfile": "Dockerfile", |
| 179 | + "tags": ["username/vote"], |
| 180 | + "target": "dev" |
| 181 | + }, |
| 182 | + "worker": { |
| 183 | + "context": "worker", |
| 184 | + "dockerfile": "Dockerfile", |
| 185 | + "tags": ["username/worker"] |
| 186 | + } |
| 187 | + } |
| 188 | +} |
| 189 | +``` |
| 190 | + |
| 191 | +As you can see, Bake has created a `default` group that includes four targets: |
| 192 | + |
| 193 | +- `seed` |
| 194 | +- `vote` |
| 195 | +- `result` |
| 196 | +- `worker` |
| 197 | + |
| 198 | +This group is created automatically from your Compose file; it includes all of |
| 199 | +your services containing a build configuration. To build this group of services |
| 200 | +with Bake, run: |
| 201 | + |
| 202 | +```console |
| 203 | +$ docker buildx bake |
| 204 | +``` |
| 205 | + |
| 206 | +### Customize the build group |
| 207 | + |
| 208 | +Start by redefining the default build group that Bake executes. The current |
| 209 | +default group includes a `seed` target — a Compose service used solely to |
| 210 | +populate the database with mock data. Since this target doesn't produce a |
| 211 | +production image, it doesn’t need to be included in the build group. |
| 212 | + |
| 213 | +To customize the build configuration that Bake uses, create a new file at the |
| 214 | +root of the repository, alongside your `compose.yaml` file, named |
| 215 | +`docker-bake.hcl`. |
| 216 | + |
| 217 | +Open the Bake file and add the following configuration: |
| 218 | + |
| 219 | +```hcl {title=docker-bake.hcl} |
| 220 | +group "default" { |
| 221 | + targets = ["vote", "result", "worker"] |
| 222 | +} |
| 223 | +``` |
| 224 | + |
| 225 | +Now, print your Bake definition again. |
| 226 | + |
| 227 | +```console |
| 228 | +$ docker buildx bake --print |
| 229 | +``` |
| 230 | + |
| 231 | +The JSON output shows that the `default` group only includes the targets you |
| 232 | +care about. |
| 233 | + |
| 234 | +```json |
| 235 | +{ |
| 236 | + "group": { |
| 237 | + "default": { |
| 238 | + "targets": ["vote", "result", "worker"] |
| 239 | + } |
| 240 | + }, |
| 241 | + "target": { |
| 242 | + "result": { |
| 243 | + "context": "result", |
| 244 | + "dockerfile": "Dockerfile", |
| 245 | + "tags": ["username/result"] |
| 246 | + }, |
| 247 | + "vote": { |
| 248 | + "context": "vote", |
| 249 | + "dockerfile": "Dockerfile", |
| 250 | + "tags": ["username/vote"], |
| 251 | + "target": "dev" |
| 252 | + }, |
| 253 | + "worker": { |
| 254 | + "context": "worker", |
| 255 | + "dockerfile": "Dockerfile", |
| 256 | + "tags": ["username/worker"] |
| 257 | + } |
| 258 | + } |
| 259 | +} |
| 260 | +``` |
| 261 | + |
| 262 | +Here, the build configuration for each target (context, tags, etc.) is picked |
| 263 | +up from the `compose.yaml` file. The group is defined by the `docker-bake.hcl` |
| 264 | +file. |
| 265 | + |
| 266 | +### Customize targets |
| 267 | + |
| 268 | +The Compose file currently defines the `dev` stage as the build target for the |
| 269 | +`vote` service. That's appropriate for the image that you would run in local |
| 270 | +development, because the `dev` stage includes additional development |
| 271 | +dependencies and configurations. For the production image, however, you'll want |
| 272 | +to target the `final` image instead. |
| 273 | + |
| 274 | +To modify the target stage used by the `vote` service, add the following |
| 275 | +configuration to the Bake file: |
| 276 | + |
| 277 | +```hcl |
| 278 | +target "vote" { |
| 279 | + target = "final" |
| 280 | +} |
| 281 | +``` |
| 282 | + |
| 283 | +This overrides the `target` property specified in the Compose file with a |
| 284 | +different value when you run the build with Bake. The other build options in |
| 285 | +the Compose file (tag, context) remain unmodified. You can verify by inspecting |
| 286 | +the build configuration for the `vote` target with `docker buildx bake --print |
| 287 | +vote`: |
| 288 | + |
| 289 | +```json |
| 290 | +{ |
| 291 | + "group": { |
| 292 | + "default": { |
| 293 | + "targets": ["vote"] |
| 294 | + } |
| 295 | + }, |
| 296 | + "target": { |
| 297 | + "vote": { |
| 298 | + "context": "vote", |
| 299 | + "dockerfile": "Dockerfile", |
| 300 | + "tags": ["username/vote"], |
| 301 | + "target": "final" |
| 302 | + } |
| 303 | + } |
| 304 | +} |
| 305 | +``` |
| 306 | + |
| 307 | +### Additional build features |
| 308 | + |
| 309 | +Production-grade builds often have different characteristics from development |
| 310 | +builds. Here are a few examples of things you might want to add for production |
| 311 | +images. |
| 312 | + |
| 313 | +Multi-platform |
| 314 | +: For local development, you only need to build images for your local platform, |
| 315 | +since those images are just going to run on your machine. But for images that |
| 316 | +are pushed to a registry, it's often a good idea to build for multiple |
| 317 | +platforms, arm64 and amd64 in particular. |
| 318 | + |
| 319 | +Attestations |
| 320 | +: [Attestations](/manuals/build/metadata/attestations/_index.md) are manifests |
| 321 | +attached to the image that describe how the image was created and what |
| 322 | +components it contains. Attaching attestations to your images helps ensure that |
| 323 | +your images follow software supply chain best practices. |
| 324 | + |
| 325 | +Annotations |
| 326 | +: [Annotations](/manuals/build/metadata/annotations.md) provide descriptive |
| 327 | +metadata for images. Use annotations to record arbitrary information and attach |
| 328 | +it to your image, which helps consumers and tools understand the origin, |
| 329 | +contents, and how to use the image. |
| 330 | + |
| 331 | +> [!TIP] |
| 332 | +> Why not just define these additional build options in the Compose file |
| 333 | +> directly? |
| 334 | +> |
| 335 | +> The `build` property in the Compose file format does not support all build |
| 336 | +> features. Additionally, some features, like multi-platform builds, can |
| 337 | +> drastically increase the time it takes to build a service. For local |
| 338 | +> development, you're better off keeping your build step simple and fast, |
| 339 | +> saving the bells and whistles for release builds. |
| 340 | + |
| 341 | +To add these properties to the images you build with Bake, update the Bake file |
| 342 | +as follows: |
| 343 | + |
| 344 | +```hcl |
| 345 | +group "default" { |
| 346 | + targets = ["vote", "result", "worker"] |
| 347 | +} |
| 348 | +
|
| 349 | +target "_common" { |
| 350 | + annotations = ["org.opencontainers.image.authors=username"] |
| 351 | + platforms = ["linux/amd64", "linux/arm64"] |
| 352 | + attest = [ |
| 353 | + "type=provenance,mode=max", |
| 354 | + "type=sbom" |
| 355 | + ] |
| 356 | +} |
| 357 | +
|
| 358 | +target "vote" { |
| 359 | + inherits = ["_common"] |
| 360 | + target = "final" |
| 361 | +} |
| 362 | +
|
| 363 | +target "result" { |
| 364 | + inherits = ["_common"] |
| 365 | +} |
| 366 | +
|
| 367 | +target "worker" { |
| 368 | + inherits = ["_common"] |
| 369 | +} |
| 370 | +``` |
| 371 | + |
| 372 | +This defines a new `_common` target that defines reusable build configuration |
| 373 | +for adding multi-platform support, annotations, and attestations to your |
| 374 | +images. The reusable target is inherited by the build targets. |
| 375 | + |
| 376 | +With these changes, building the project with Bake produces three sets of |
| 377 | +multi-platform images for the `linux/amd64` and `linux/arm64` architectures. |
| 378 | +Each image is decorated with an author annotation, and both SBOM and provenance |
| 379 | +attestation records. |
| 380 | + |
| 381 | +## Conclusions |
| 382 | + |
| 383 | +The pattern demonstrated in this guide provides a useful approach for managing |
| 384 | +production-ready Docker images in projects using Docker Compose. Using Bake |
| 385 | +gives you access to all the powerful features of Buildx and BuildKit, and also |
| 386 | +helps separate your development and build configuration in a reasonable way. |
| 387 | + |
| 388 | +### Further reading |
| 389 | + |
| 390 | +For more information about how to use Bake, check out these resources: |
| 391 | + |
| 392 | +- [Bake documentation](/manuals/build/bake/_index.md) |
| 393 | +- [Building with Bake from a Compose file](/manuals/build/bake/compose-file.md) |
| 394 | +- [Bake file reference](/manuals/build/bake/reference.md) |
| 395 | +- [Mastering multi-platform builds, testing, and more with Docker Buildx Bake](/guides/bake/index.md) |
| 396 | +- [Bake GitHub Action](https://github.com/docker/bake-action) |
0 commit comments