testcontainers-go icon indicating copy to clipboard operation
testcontainers-go copied to clipboard

[Bug]: Building Multiplatform Dockerfile

Open arauin opened this issue 5 months ago • 2 comments

Testcontainers version

v0.38.0

Using the latest Testcontainers version?

Yes

Host OS

Linux

Host arch

amd64

Go version

1.24.5

Docker version

Client:
 Version:           28.3.0
 API version:       1.51
 Go version:        go1.24.4
 Git commit:        38b7060a21
 Built:             Wed Jun 25 15:40:54 2025
 OS/Arch:           linux/amd64
 Context:           default

Server:
 Engine:
  Version:          28.3.0
  API version:      1.51 (minimum version 1.24)
  Go version:       go1.24.4
  Git commit:       265f709647
  Built:            Wed Jun 25 15:40:54 2025
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v2.1.3
  GitCommit:        c787fb98911740dd3ff2d0e45ce88cdf01410486.m
 runc:
  Version:          1.3.0
  GitCommit:        
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

Docker info

Client:
 Version:    28.3.0
 Context:    default
 Debug Mode: false
 Plugins:
  buildx: Docker Buildx (Docker Inc.)
    Version:  0.25.0
    Path:     /usr/lib/docker/cli-plugins/docker-buildx
  compose: Docker Compose (Docker Inc.)
    Version:  2.38.2
    Path:     /usr/lib/docker/cli-plugins/docker-compose

Server:
 Containers: 17
  Running: 0
  Paused: 0
  Stopped: 17
 Images: 60
 Server Version: 28.3.0
 Storage Driver: overlay2
  Backing Filesystem: btrfs
  Supports d_type: true
  Using metacopy: true
  Native Overlay Diff: false
  userxattr: false
 Logging Driver: json-file
 Cgroup Driver: systemd
 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
 Swarm: inactive
 Runtimes: io.containerd.runc.v2 runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: c787fb98911740dd3ff2d0e45ce88cdf01410486.m
 runc version: 
 init version: de40ad0
 Security Options:
  seccomp
   Profile: builtin
  cgroupns
 Kernel Version: 6.15.6-arch1-1
 Operating System: Arch Linux
 OSType: linux
 Architecture: x86_64
 CPUs: 16
 Total Memory: 23.23GiB
 Name: t14
 ID: 64068bc9-c110-4ea9-b6b2-9b0c968305c8
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 Experimental: false
 Insecure Registries:
  ::1/128
  127.0.0.0/8
 Live Restore Enabled: false

What happened?

When using GenericContainerRequest with FromDockerFile https://golang.testcontainers.org/features/build_from_dockerfile/

And a Dockerfile which has the FROM --platform=$BUILDPLATFORM option set https://docs.docker.com/reference/dockerfile/#from

e.g. from https://docs.docker.com/build/building/multi-platform/#cross-compiling-a-go-application the updated example

# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM golang:alpine AS build
ARG TARGETOS
ARG TARGETARCH
WORKDIR /app
ADD https://github.com/dvdksn/buildme.git#eb6279e0ad8a10003718656c6867539bd9426ad8 .
RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o server .

FROM alpine
COPY --from=build /app/server /server
ENTRYPOINT ["/server"]

The platform is not properly propagated to docker and the build fails

Relevant log output

create container: build image: failed to parse platform : "" is an invalid OS component of "": OSAndVersion specifier component must match "^([A-Za-z0-9_-]+)(?:\\(([A-Za-z0-9_.-]*)\\))?$": invalid argument

Additional information

I tried adding the buildplatform in various ways, but it has not resolved the issue

func TestCustomerRepository(t *testing.T) {
	ctx := context.Background()
	buildPlatform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)

	os.Setenv("BUILDPLATFORM", buildPlatform)

	req := testcontainers.GenericContainerRequest{
		ContainerRequest: testcontainers.ContainerRequest{
			ImagePlatform: buildPlatform,
			Env:           map[string]string{"BUILDPLATFORM": buildPlatform},
			FromDockerfile: testcontainers.FromDockerfile{
				Context:    "../..",
				Dockerfile: "Dockerfile",
				BuildOptionsModifier: func(opts *build.ImageBuildOptions) {
					println(opts.Dockerfile)
					opts.Platform = "linux/amd64"
				},
				BuildArgs: map[string]*string{"TARGETPLATFORM": &buildPlatform},
			},
		},
	}

	ctr, err := testcontainers.GenericContainer(ctx, req)
	testcontainers.CleanupContainer(t, ctr)
	require.NoError(t, err)
}

arauin avatar Aug 11 '25 19:08 arauin

https://github.com/advisoreeu/pgdumps3/blob/0794237c1ffa2423161c9aa5bc94530d4061eccb/tests/e2e/e2e.go#L77-L102

My workaround is to set the platform before using it with testcontainers.

arauin avatar Aug 11 '25 22:08 arauin

Hi. I have literally just come across this same thing by chance, and I finally found the answer. I have documented it here .

You just need to do

BuildOptionsModifier: func(opts *build.ImageBuildOptions) {
       opts.Version = build.BuilderBuildKit // <-- this
       opts.Platform = "linux/amd64"
},

All of the modern buildkit related syntax/features like --platform and $BUILDPLATFORM will probably work as you expect in the Dockerfile with this change :).

Using buildkit is not documented in this project, and as time has gone on and docker's core docs have switched over -- its leading to some confusion about why stuff doesn't behave as described.

adamscybot avatar Aug 12 '25 07:08 adamscybot