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.Context

View on GitHub (pinned to ddc4b044b6)

Solutions

  1. Enable BuildKit: unset DOCKER_BUILDKIT, unset COMPOSE_BAKE, and confirm buildx is installed
  2. If the legacy builder is mandatory, remove build.privileged and restructure the build to avoid privileged steps (do them at runtime instead)
  3. 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

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


AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15). Data as JSON: /api/errors/4f13c8121d00200f. Report an issue: GitHub.