Skip to content

bake: extra-hosts causes build cache miss due to nondeterminstic ordering #3788

@blowfishpro

Description

@blowfishpro

Contributing guidelines

I've found a bug and checked that ...

  • ... the documentation does not mention anything about my problem
  • ... there are no open or closed issues that are related to my problem

Description

When passing extra-hosts in a bakefile, they end up being used in a nondeterministic order, causing cache misses on subsequent builds even when all the inputs are the same.

Expected behaviour

Building again with the same build inputs results in a cache hit. Extra hosts are used in a deterministic order.

Actual behaviour

Extra hosts are used in a nondeterminstic order, resulting in a cache miss. The ordering is visible in /etc/hosts.

Buildx version

github.com/docker/buildx v0.32.1 Homebrew

Docker info

Details
Client: Docker Engine - Community
 Version:    29.3.0
 Context:    desktop-linux
 Debug Mode: false
 Plugins:
  agent: Docker AI Agent Runner (Docker Inc.)
    Version:  v1.39.0
    Path:     /Users/work/.docker/cli-plugins/docker-agent
  ai: Docker AI Agent - Ask Gordon (Docker Inc.)
    Version:  v1.20.2
    Path:     /Users/work/.docker/cli-plugins/docker-ai
  buildx: Docker Buildx (Docker Inc.)
    Version:  v0.32.1
    Path:     /opt/homebrew/lib/docker/cli-plugins/docker-buildx
  compose: Docker Compose (Docker Inc.)
    Version:  5.1.0
    Path:     /opt/homebrew/lib/docker/cli-plugins/docker-compose
  debug: Get a shell into any image or container (Docker Inc.)
    Version:  0.0.47
    Path:     /Users/work/.docker/cli-plugins/docker-debug
  desktop: Docker Desktop commands (Docker Inc.)
    Version:  v0.3.0
    Path:     /Users/work/.docker/cli-plugins/docker-desktop
  dhi: CLI for managing Docker Hardened Images (Docker Inc.)
    Version:  v0.0.2
    Path:     /Users/work/.docker/cli-plugins/docker-dhi
  extension: Manages Docker extensions (Docker Inc.)
    Version:  v0.2.31
    Path:     /Users/work/.docker/cli-plugins/docker-extension
  init: Creates Docker-related starter files for your project (Docker Inc.)
    Version:  v1.4.0
    Path:     /Users/work/.docker/cli-plugins/docker-init
  mcp: Docker MCP Plugin (Docker Inc.)
    Version:  v0.40.3
    Path:     /Users/work/.docker/cli-plugins/docker-mcp
  model: Docker Model Runner (Docker Inc.)
    Version:  v1.1.28
    Path:     /Users/work/.docker/cli-plugins/docker-model
  offload: Docker Offload (Docker Inc.)
    Version:  v0.5.81
    Path:     /Users/work/.docker/cli-plugins/docker-offload
  pass: Docker Pass Secrets Manager Plugin (beta) (Docker Inc.)
    Version:  v0.0.24
    Path:     /Users/work/.docker/cli-plugins/docker-pass
  sandbox: Docker Sandbox (Docker Inc.)
    Version:  v0.12.0
    Path:     /Users/work/.docker/cli-plugins/docker-sandbox
  sbom: View the packaged-based Software Bill Of Materials (SBOM) for an image (Anchore Inc.)
    Version:  0.6.0
    Path:     /Users/work/.docker/cli-plugins/docker-sbom
  scout: Docker Scout (Docker Inc.)
    Version:  v1.20.3
    Path:     /Users/work/.docker/cli-plugins/docker-scout

Server:
 Containers: 0
  Running: 0
  Paused: 0
  Stopped: 0
 Images: 47
 Server Version: 29.3.1
 Storage Driver: overlayfs
  driver-type: io.containerd.snapshotter.v1
 Logging Driver: json-file
 Cgroup Driver: cgroupfs
 Cgroup Version: 2
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog
 CDI spec directories:
  /etc/cdi
  /var/run/cdi
 Discovered Devices:
  cdi: docker.com/gpu=webgpu
 Swarm: inactive
 Runtimes: io.containerd.runc.v2 runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: dea7da592f5d1d2b7755e3a161be07f43fad8f75
 runc version: v1.3.4-0-gd6d73eb8
 init version: de40ad0
 Security Options:
  seccomp
   Profile: builtin
  cgroupns
 Kernel Version: 6.12.76-linuxkit
 Operating System: Docker Desktop
 OSType: linux
 Architecture: aarch64
 CPUs: 10
 Total Memory: 7.653GiB
 Name: docker-desktop
 ID: fb74997c-ef33-4a9e-957e-ae728cdf9232
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 HTTP Proxy: http.docker.internal:3128
 HTTPS Proxy: http.docker.internal:3128
 No Proxy: hubproxy.docker.internal
 Labels:
  com.docker.desktop.address=unix:///Users/work/Library/Containers/com.docker.docker/Data/docker-cli.sock
 Experimental: false
 Insecure Registries:
  hubproxy.docker.internal:5555
  ::1/128
  127.0.0.0/8
 Live Restore Enabled: false
 Firewall Backend: iptables

Builders list

NAME/NODE           DRIVER/ENDPOINT     STATUS    BUILDKIT   PLATFORMS
default             docker                                   
 \_ default          \_ default         running   v0.28.1    linux/amd64 (+2), linux/arm64, linux/ppc64le, linux/s390x, (2 more)
desktop-linux*      docker                                   
 \_ desktop-linux    \_ desktop-linux   running   v0.28.1    linux/amd64 (+2), linux/arm64, linux/ppc64le, linux/s390x, (2 more)

(these appear to be the same builder I'm unsure as to why Docker Desktop includes it twice)

Configuration

target "default" {
  dockerfile-inline = <<-EOF
    FROM busybox:latest

    RUN cat /etc/hosts
  EOF

  extra-hosts = {
    "sub0.example.com" = "127.0.0.1"
    "sub1.example.com" = "127.0.0.1"
    "sub2.example.com" = "127.0.0.1"
    "sub3.example.com" = "127.0.0.1"
    "sub4.example.com" = "127.0.0.1"
    "sub5.example.com" = "127.0.0.1"
    "sub6.example.com" = "127.0.0.1"
    "sub7.example.com" = "127.0.0.1"
    "sub8.example.com" = "127.0.0.1"
    "sub9.example.com" = "127.0.0.1"
  }
}

Build logs

first build:


#0 building with "desktop-linux" instance using docker driver

#1 [internal] load local bake definitions
#1 reading docker-bake.hcl 497B / 497B done
#1 DONE 0.0s

#2 [internal] load build definition from Dockerfile
#2 transferring dockerfile: 77B done
#2 DONE 0.0s

#3 [internal] load metadata for docker.io/library/busybox:latest
#3 DONE 0.0s

#4 [internal] load .dockerignore
#4 transferring context: 2B done
#4 DONE 0.0s

#5 [1/2] FROM docker.io/library/busybox:latest@sha256:d82f458899c9696cb26a7c02d5568f81c8c8223f8661bb2a7988b269c8b9051e
#5 resolve docker.io/library/busybox:latest@sha256:d82f458899c9696cb26a7c02d5568f81c8c8223f8661bb2a7988b269c8b9051e done
#5 DONE 0.0s

#6 [2/2] RUN cat /etc/hosts
#6 0.149 127.0.0.1	localhost buildkitsandbox
#6 0.149 ::1	localhost ip6-localhost ip6-loopback
#6 0.149 127.0.0.1	sub5.example.com
#6 0.149 127.0.0.1	sub6.example.com
#6 0.149 127.0.0.1	sub8.example.com
#6 0.149 127.0.0.1	sub0.example.com
#6 0.149 127.0.0.1	sub3.example.com
#6 0.149 127.0.0.1	sub1.example.com
#6 0.149 127.0.0.1	sub4.example.com
#6 0.149 127.0.0.1	sub9.example.com
#6 0.149 127.0.0.1	sub7.example.com
#6 0.149 127.0.0.1	sub2.example.com
#6 DONE 0.2s

second build:

#0 building with "desktop-linux" instance using docker driver

#1 [internal] load local bake definitions
#1 reading docker-bake.hcl 497B / 497B done
#1 DONE 0.0s

#2 [internal] load build definition from Dockerfile
#2 transferring dockerfile: 77B done
#2 DONE 0.0s

#3 [internal] load metadata for docker.io/library/busybox:latest
#3 DONE 0.0s

#4 [internal] load .dockerignore
#4 transferring context: 2B done
#4 DONE 0.0s

#5 [1/2] FROM docker.io/library/busybox:latest@sha256:d82f458899c9696cb26a7c02d5568f81c8c8223f8661bb2a7988b269c8b9051e
#5 resolve docker.io/library/busybox:latest@sha256:d82f458899c9696cb26a7c02d5568f81c8c8223f8661bb2a7988b269c8b9051e done
#5 CACHED

#6 [2/2] RUN cat /etc/hosts
#6 0.097 127.0.0.1	localhost buildkitsandbox
#6 0.097 ::1	localhost ip6-localhost ip6-loopback
#6 0.097 127.0.0.1	sub2.example.com
#6 0.097 127.0.0.1	sub3.example.com
#6 0.097 127.0.0.1	sub4.example.com
#6 0.097 127.0.0.1	sub7.example.com
#6 0.097 127.0.0.1	sub1.example.com
#6 0.097 127.0.0.1	sub5.example.com
#6 0.097 127.0.0.1	sub6.example.com
#6 0.097 127.0.0.1	sub8.example.com
#6 0.097 127.0.0.1	sub0.example.com
#6 0.097 127.0.0.1	sub9.example.com
#6 DONE 0.1s

I would expect the RUN cat /etc/hosts step to be cached on the second build, but it is evidently not.

Additional info

This only appears to affect bake. Passing the --add-hosts flag to docker buildx build multiple times appears to result in the hosts being used in order.

The cause is possibly that golang map iteration order is nondeterministic (by design), causing this code to generate a nondeterministic list.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions