docker/compose · error
the classic builder doesn't support privileged mode, set DOC
Error message
the classic builder doesn't support privileged mode, set DOCKER_BUILDKIT=1 to use BuildKit
What it means
On the classic (non-BuildKit) build path, doBuildImage rejects build.privileged: true. Privileged builds are a BuildKit feature (running the builder with extended permissions for e.g. nested container runtimes); the legacy API has no equivalent switch, so the request is refused with guidance to set DOCKER_BUILDKIT=1.
Source
Thrown at pkg/compose/build_classic.go:137
}
}
return imageIDs, err
}
//nolint:gocyclo
func (s *composeService) doBuildImage(ctx context.Context, project *types.Project, service types.ServiceConfig, options api.BuildOptions) (string, error) {
var (
buildCtx io.ReadCloser
dockerfileCtx io.ReadCloser
contextDir string
relDockerfile string
)
if len(service.Build.Platforms) > 1 {
return "", fmt.Errorf("the classic builder doesn't support multi-arch build, set DOCKER_BUILDKIT=1 to use BuildKit")
}
if service.Build.Privileged {
return "", fmt.Errorf("the classic builder doesn't support privileged mode, set DOCKER_BUILDKIT=1 to use BuildKit")
}
if len(service.Build.AdditionalContexts) > 0 {
return "", fmt.Errorf("the classic builder doesn't support additional contexts, set DOCKER_BUILDKIT=1 to use BuildKit")
}
if len(service.Build.SSH) > 0 {
return "", fmt.Errorf("the classic builder doesn't support SSH keys, set DOCKER_BUILDKIT=1 to use BuildKit")
}
if len(service.Build.Secrets) > 0 {
return "", fmt.Errorf("the classic builder doesn't support secrets, set DOCKER_BUILDKIT=1 to use BuildKit")
}
if service.Build.Labels == nil {
service.Build.Labels = make(map[string]string)
}
service.Build.Labels[api.ImageBuilderLabel] = "classic"
dockerfileName := dockerFilePath(service.Build.Context, service.Build.Dockerfile)
specifiedContext := service.Build.ContextView on GitHub (pinned to ddc4b044b6)
Solutions
- Enable BuildKit: unset DOCKER_BUILDKIT, unset COMPOSE_BAKE, and confirm buildx is installed
- If the legacy builder is mandatory, remove build.privileged and restructure the build to avoid privileged steps (do them at runtime instead)
- Install/upgrade the buildx plugin so compose can select the BuildKit path automatically
Example fix
# before $ DOCKER_BUILDKIT=0 docker compose build # build.privileged: true # after $ unset DOCKER_BUILDKIT $ docker compose build
Defensive patterns
Strategy: validation
Validate before calling
// Guard before a classic-path build:
for name, svc := range project.Services {
if svc.Build != nil && svc.Build.Privileged && classicBuilder() {
return fmt.Errorf("service %s requires BuildKit for build.privileged", name)
}
} Prevention
- Treat build.privileged as BuildKit-only; do not attempt legacy-builder fallbacks
- Move privileged operations into runtime containers when BuildKit is unavailable
- Keep an explicit buildx install step in environment setup
When it happens
Trigger: A service sets build.privileged: true and the build routes to the classic builder — DOCKER_BUILDKIT=0 in the environment, COMPOSE_BAKE=false, or buildx unavailable.
Common situations: Building images that launch containers inside the build (systemd, K8s-in-docker, qemu binfmt installers) with privileged enabled, on hosts where BuildKit was disabled to dodge a proxy/cache issue; air-gapped environments lacking buildx.
Related errors
- the classic builder doesn't support multi-arch build, set DO
- the classic builder doesn't support additional contexts, set
- the classic builder doesn't support SSH keys, set DOCKER_BUI
- the classic builder doesn't support secrets, set DOCKER_BUIL
- building from STDIN is not supported
AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15).
Data as JSON: /api/errors/4f13c8121d00200f.
Report an issue: GitHub.