Using Bake with additional contexts

In addition to the main context key that defines the build context, each target can also define additional named contexts with a map defined with key contexts. These values map to the --build-context flag in the build command.

Inside the Dockerfile these contexts can be used with the FROM instruction or --from flag.

Supported context values are:

  • Local filesystem directories
  • Container images
  • Git URLs
  • HTTP URLs
  • Name of another target in the Bake file

Pinning alpine image

# syntax=docker/dockerfile:1
FROM alpine
RUN echo "Hello world"
# docker-bake.hcl
target "app" {
  contexts = {
    alpine = "docker-image://alpine:3.13"
  }
}

Using a secondary source directory

# syntax=docker/dockerfile:1
FROM scratch AS src

FROM golang
COPY --from=src . .
# docker-bake.hcl
target "app" {
  contexts = {
    src = "../path/to/source"
  }
}

Using a target as a build context

To use a result of one target as a build context of another, specify the target name with target: prefix.

# syntax=docker/dockerfile:1
FROM baseapp
RUN echo "Hello world"
# docker-bake.hcl
target "base" {
  dockerfile = "baseapp.Dockerfile"
}

target "app" {
  contexts = {
    baseapp = "target:base"
  }
}

In most cases you should just use a single multi-stage Dockerfile with multiple targets for similar behavior. This case is only recommended when you have multiple Dockerfiles that can't be easily merged into one.

Deduplicate context transfer

When you build targets concurrently, using groups, build contexts are loaded independently for each target. If the same context is used by multiple targets in a group, that context is transferred once for each time it's used. This can result in significant impact on build time, depending on your build configuration. For example, say you have a Bake file that defines the following group of targets:

group "default" {
  targets = ["target1", "target2"]
}

target "target1" {
  target = "target1"
  context = "."
}

target "target2" {
  target = "target2"
  context = "."
}

In this case, the context . is transferred twice when you build the default group: once for target1 and once for target2.

If your context is small, and if you are using a local builder, duplicate context transfers may not be a big deal. But if your build context is big, or you have a large number of targets, or you're transferring the context over a network to a remote builder, context transfer becomes a performance bottleneck.

To avoid transferring the same context multiple times, you can define a named context that only loads the context files, and have each target that needs those files reference that named context. For example, the following Bake file defines a named target ctx, which is used by both target1 and target2:

group "default" {
  targets = ["target1", "target2"]
}

target "ctx" {
  context = "."
  target = "ctx"
}

target "target1" {
  target = "target1"
  contexts = {
    ctx = "target:ctx"
  }
}

target "target2" {
  target = "target2"
  contexts = {
    ctx = "target:ctx"
  }
}

The named context ctx represents a Dockerfile stage, which copies the files from its context (.). Other stages in the Dockerfile can now reference the ctx named context and, for example, mount its files with --mount=from=ctx.

FROM scratch AS ctx
COPY --link . .

FROM golang:alpine AS target1
WORKDIR /work
RUN --mount=from=ctx \
    go build -o /out/client ./cmd/client \

FROM golang:alpine AS target2
WORKDIR /work
RUN --mount=from=ctx \
    go build -o /out/server ./cmd/server