

Docker Bake¶
Table of Contents¶
- 1. Introduction
- What is Docker
Docker Bake? - What is Docker
BuildKit? - Key Features
- Comparison: Docker Bake vs Docker Compose
- Docker Bake syntax
- Common Commands
- Advanced Features
- Multi-Stage Builds with Matrix
- Build Secrets
- Cache Configuration / Build Caching
- Cache Configuration / Build Caching Workflow
- Target Inheritance
1. Introduction¶
What is Docker Docker Bake?¶
-
👍
Docker Bakeis a feature ofBuildKit/Docker Buildxthat allows you to define and orchestrate complex builds using a high-level build tool that uses to build Docker images defined in configuration files. -
👍
Docker Bakeis written in a declarative language (HCL,JSON, orYAML). -
👍
Docker Bakeis designed for building multiple images or targets concurrently, making it ideal for monorepos, microservices, or any project with severalDockerfiles. -
👍
Docker Bakeextends beyond singleDockerfiles to support complex build scenarios with multiple targets, platforms, and configurations. -
👍
Docker Bakeis designed for CI/CD pipelines, multi-platform builds, and complex build workflows.
What is Docker BuildKit?¶
-
👍
BuildKitis a modern build subsystem for Docker that improves performance, storage management, and caching -
👍
BuildKitis the engine behindDocker Bake, enabling advanced features like multi-platform builds, caching, and build secrets -
👍
BuildKitis the default build engine for Docker, replacing the traditional docker build mechanism. -
👍
BuildKitallows for parallel builds, advanced caching, and better resource utilization. -
👍
BuildKitsupports advanced features like build secrets, SSH forwarding, and more. -
👍
BuildKitis a key component of the new Docker Engine 20.10 release, which includes thebuildkitsubcommand and thedocker buildxcommand.
Key Features¶
| Feature | Description |
|---|---|
| Advanced BuildKit Features | Leverage BuildKit for caching, multi-platform builds, and advanced outputs. |
| Advanced caching | Sophisticated caching strategies for faster builds |
| Build matrix | Define multiple build configurations and targets |
| Build secrets | Securely pass sensitive information during builds |
| Cache configuration | Fine-tune caching strategies for faster builds |
| CI/CD integration | Ideal for automated build pipelines and workflows |
| Concurrent Builds | Build multiple images in parallel with a single command. |
| Custom build arguments | Specify different build arguments for each target |
| Custom build contexts | Specify different directories for each target |
| Custom Dockerfiles | Use different Dockerfiles for each target |
| Declarative Build Configuration | Define build targets, groups, arguments, tags, and more in a single file. |
| Declarative syntax | Use HCL, JSON, or YAML to define build configurations |
| Extensibility | Supports custom build steps and plugins |
| Flexible File Formats | Supports HCL (HashiCorp Configuration Language), JSON, and YAML (Compose extension). |
| Group targets | Organize multiple build targets into logical groups |
| Inheritance | Reuse common configurations across multiple targets |
| Integration with Docker Compose | You can use Compose files as Bake files for seamless migration. |
| Matrix Builds | Easily define build matrices for different platforms, versions, or configurations. |
| Multi-platform builds | Build for multiple architectures simultaneously (ARM, AMD64, etc.) |
| Variable interpolation | Use variables and functions for dynamic configurations |
Comparison: Docker Bake vs Docker Compose¶
| Feature | Docker Bake | Docker Compose |
|---|---|---|
| Purpose | Build orchestration | Service orchestration |
| File Format | HCL, JSON, YAML (Compose) | YAML |
| Parallel Builds | Yes | No |
| Multi-platform | Yes (via BuildKit) | Limited |
| Service Deploy | No | Yes |
| Caching | Advanced BuildKit caching | Basic |
| Inheritance | Yes | No |
| CI/CD | Ideal for CI/CD pipelines | Limited |
| Extensibility | Supports custom build steps | Limited |
| Advanced | Advanced build features | Basic |
[!WARNING]
Docker Bakerequires Docker Engine v20.10.0 or higher.
Docker Bakerequires BuildKit for advanced build features.
Docker Bake syntax¶
[!IMPORTANT]
docker-bake.hclis a declarative build configuration file format.
- Here’s a basic
docker-bake.hclfile structure: - It uses the HashiCorp Configuration Language (HCL) syntax for defining build configurations.
- This example defines two targets:
frontendandbackend-api, each with its own context, Dockerfile, tags, platforms, and build arguments.
# Define variables
variable "TAG" {
default = "latest"
}
# Define a user variable for tagging images
# This can be your Docker Hub username or any identifier
variable "USER" {
default = "username"
}
# Define a group of targets for default builds
group "default" {
targets = ["frontend", "backend-api"]
}
# Frontend service
target "frontend" {
# Directory containing the frontend code
context = "./frontend"
# Use a specific Dockerfile for production
dockerfile = "Dockerfile.prod"
# Tag the image with user and tag
tags = ["${USER}/frontend:${TAG}"]
# Build for multiple architectures
platforms = ["linux/amd64", "linux/arm64"]
# Pass build-time variables to the Dockerfile
args = {
# Set Node.js environment to production
NODE_ENV = "production"
# Set API URL for the frontend
API_URL = "https://api.codewizard.com"
}
}
# Node.js backend service
target "backend-api" {
context = "./backend"
dockerfile = "Dockerfile"
tags = ["${USER}/backend:${TAG}"]
platforms = ["linux/amd64", "linux/arm64"]
args = {
NODE_ENV = "production"
}
}
- Here is another example of a
Docker Bakeconfiguration file: in JSON
{
"variable": {
"TAG": {
"default": "latest"
},
"USER": {
"default": "username"
}
},
"group": {
"default": {
"targets": ["frontend", "backend-api"]
}
},
"target": {
"frontend": {
"context": "./frontend",
"dockerfile": "Dockerfile.prod",
"tags": ["${USER}/frontend:${TAG}"],
"platforms": ["linux/amd64", "linux/arm64"],
"args": {
"NODE_ENV": "production",
"API_URL": "https://api.codewizard.com"
}
},
"backend-api": {
"context": "./backend",
"dockerfile": "Dockerfile",
"tags": ["${USER}/backend:${TAG}"],
"platforms": ["linux/amd64", "linux/arm64"],
"args": {
"NODE_ENV": "production"
}
}
}
}
Common Commands¶
| Feature | Description | Command |
|---|---|---|
| Build Default Group | Build all targets in the default group | docker buildx bake |
| Build Specific Target | Build a specific target by name | docker buildx bake <target> |
| Build with Custom Variables | Override variables for a specific build | docker buildx bake --set <target>.<var>=<value> |
| Build and Push | Build images and push them to a registry | docker buildx bake --push |
| Build for Specific Platform | Build images for a specific platform architecture | docker buildx bake --platform <platform> |
| View Build Configuration | Print the effective build configuration | docker buildx bake --print |
| Build with Custom File | Use a custom bake file instead of the default | docker buildx bake -f <file> |
Advanced Features¶
Multi-Stage Builds with Matrix¶
- What is a Build Matrix?
- A build matrix allows you to create multiple image variations from a single configuration.
- This is useful when you need to build multiple versions of your application or service.
- For example, you might need to build different versions of your application for different environments (e.g., development, staging, production).
- By using a build matrix, you can specify multiple combinations of values for each configuration option.
-
Instead of writing separate targets for each combination, Docker Bake automatically generates all possible combinations.
-
Example: Building multiple Node.js versions for different environments
target "app" {
matrix = {
version = ["18", "20"]
env = ["dev", "prod"]
}
name = "app-node${version}-${env}"
context = "."
dockerfile = "Dockerfile"
tags = ["myapp:node${version}-${env}"]
args = {
NODE_VERSION = "${version}"
NODE_ENV = "${env}"
}
}
- Quiz: What will be the output of the above example?
Answer
- app-node18-dev
- app-node18-prod
- app-node20-dev
- app-node20-prod
- Each build will use the specified Node.js version and environment. - The `name` field dynamically constructs the image name based on the matrix values. - The `args` field passes the Node.js version and environment as build arguments to the Dockerfile. - The `tags` field tags the built images with the corresponding Node.js version and environment. - The `context` and `dockerfile` fields specify the build context and Dockerfile to use for all builds. - The `matrix` field defines the combinations of `version` and `env` to build.
Build Secrets¶
Build secretsallow you to securely pass sensitive information (like API keys, passwords, or certificates) to your Docker build process without including them in the final image or build history.- This is particularly useful for keeping sensitive data out of your Docker images and ensuring that it is only available during the build process.
Build secretscan be stored in a secure location, such as a secure vault or a secret management service, and then referenced during the build process.Build secretsare mounted into the build environment at runtime, allowing you to use them without exposing them in the final image.Build secretsare not stored in the final image layers, ensuring that sensitive information is not leaked.Build secretsare only available during the build process and are not accessible to the final image or container.Build secretsare defined in thedocker-bake.hclfile using thesecretfield.- You can use the
--secretflag to pass secrets to the build process. -
Secrets can also be defined in the Dockerfile using the
RUN --mount=type=secretsyntax. -
Example: Using a secret file during build
target "secure-app" {
context = "."
dockerfile = "Dockerfile"
secret = [
"id=mysecret,src=./secret.txt"
]
tags = ["myapp:secure"]
}
Dockerfile:
FROM node:18
# Use the secret during build (it wont be in the final image)
# as mentioned and explained above
RUN --mount=type=secret,id=mysecret \
API_KEY=$(cat /run/secrets/mysecret) && \
npm install --registry=https://registry.example.com
Cache Configuration / Build Caching¶
Docker Bakesupports advanced caching strategies to speed up builds and reduce resource usage.- Caching allows you to reuse previously built layers, which can significantly speed up the build process
Docker Bakecan use different cache sources, such as local files, remote registries, or GitHub Actions cache.- Caching can be configured using the
cache-fromandcache-tofields in thedocker-bake.hclfile. - Caching can also be configured using the
--cache-fromand--cache-toflags when running thedocker buildx bakecommand. - Caching can be used to speed up builds by reusing previously built layers, which can significantly reduce build times.
- Caching can also be used to reduce resource usage by avoiding unnecessary rebuilds of unchanged layers.
- Caching can be used to share build layers between different builds, which can further speed up the build process.
- Caching can be used to optimize the build process by reusing previously built layers, which can significantly reduce build times.
- Caching can reduce build times from minutes to seconds for unchanged code.
- Example: Using GitHub Actions cache
target "cached-app" {
context = "."
dockerfile = "Dockerfile"
cache-from = ["type=gha"]
cache-to = ["type=gha,mode=max"]
tags = ["myapp:cached"]
}
Cache Types:
| Type | Description |
|---|---|
type=gha | GitHub Actions cache (for CI/CD) |
type=local | Local filesystem cache |
type=registry | Docker registry cache |
Cache Configuration / Build Caching Workflow¶
mermaid flowchart TD A[First build] -->|Takes full time, saves cache| B[Second build] B -->|Reuses cached layers, builds only changed parts| C[Result] C -->|Significantly faster build times| D[End]
Target Inheritance¶
Docker Bakeallows you to create a base configuration and reuse it across multiple targets.- This follows the DRY (Don’t Repeat Yourself) principle - define common settings once and inherit them.
Example: Sharing common settings across multiple builds
| Setting | Description |
|---|---|
target "base" | This is a base target that defines common build settings |
context = "." | Uses the current directory as the build context |
dockerfile = "Dockerfile" | Uses the default Dockerfile |
platforms = ["linux/amd64", "linux/arm64"] | Builds for both AMD64 and ARM64 architectures |
tags = ["myapp:latest"] | Sets the default tag for the image |
cache-from = ["type=gha"] | Uses GitHub Actions cache for the build |
cache-to = ["type=gha,mode=max"] | Saves the cache to GitHub Actions cache with |
# Base target with common settings
target "base" {
context = "."
dockerfile = "Dockerfile"
platforms = ["linux/amd64", "linux/arm64"]
tags = ["myapp:latest"]
cache-from = ["type=gha"]
cache-to = ["type=gha,mode=max"]
}
# Example: Using inheritance to define common settings (1)
target "web" {
inherits = ["base"]
tags = ["myapp/web:latest"]
target = "web"
}
# Example: Using inheritance to define common settings (2)
target "api" {
inherits = ["base"]
tags = ["myapp/api:latest"]
target = "api"
}
How it works:
mermaid flowchart TD A[Base target] -->|Defines common settings | B[Child targets] B -->|Inherit from base and add their specific settings| C[Result] C -->|Both web and api targets automatically get the same platforms and build context| D[End]
Benefits:
- Consistency: Sharing - ensures consistent build configurations
- Reusability: All targets use the same base configuration
- Maintainability: Supports multiple targets with the same settings platforms in one place, affects all children
- Cleaner code: No repetitive configuration
- DRY principle: Avoids duplication of common settings
- Flexibility: Each target can still have its own specific settings
- Scalability: Easily add new targets with shared settings
