#7081·buildkit

In-process gateway forwarder drops NewContainer platform

Author: gmarmstrongCreated Aug 26, 2026Updated Aug 28, 2026
Labelsstatus/triage

Contributing guidelines and issue reporting guide

Well-formed report checklist

  • I have found a bug that the documentation does not mention anything about my problem
  • I have found a bug that there are no open or closed issues that are related to my problem
  • I have provided version/information about my environment and done my best to provide a reproducer

Description of bug

Bug description

frontend/gateway/forwarder.BridgeClient.NewContainer does not copy client.NewContainerRequest.Platform to container.NewContainerRequest. The container then uses the runtime.GOOS and runtime.GOARCH values in the daemon. The protobuf/gRPC gateway path copies the requested platform.

This only affects BuildKit applications that register an in-process frontend using forwarder.NewGatewayForwarder, so the built-in Dockerfile frontend (which does not call NewContainer) is unaffected. The defect can affect a BuildKit application that registers an in-process frontend with forwarder.NewGatewayForwarder.

I would expect each NewContainer implementation to preserve the requested platform.

Reproduction

Create frontend/gateway/forwarder/newcontainer_platform_test.go:

Reproducer
go
package forwarder_test

import (
	"context"
	"reflect"
	"runtime"
	"testing"

	"github.com/moby/buildkit/cache"
	bkclient "github.com/moby/buildkit/client"
	gwclient "github.com/moby/buildkit/frontend/gateway/client"
	"github.com/moby/buildkit/frontend/gateway/forwarder"
	"github.com/moby/buildkit/solver/pb"
)

type workers struct{}

func (workers) DefaultCacheManager() (cache.Manager, error) { return nil, nil }
func (workers) WorkerInfos() []bkclient.WorkerInfo          { return nil }

func TestNewContainerForwardsPlatform(t *testing.T) {
	ctx := context.Background()

	c, err := forwarder.LLBBridgeToGatewayClient(
		ctx, nil, nil, nil, nil, workers{}, "", nil,
	)
	if err != nil {
		t.Fatal(err)
	}

	targetOS := "windows"
	if runtime.GOOS == targetOS {
		targetOS = "linux"
	}

	targetArch := "amd64"
	if runtime.GOARCH == targetArch {
		targetArch = "arm64"
	}

	want := &pb.Platform{
		OS:           targetOS,
		Architecture: targetArch,
	}

	ctr, err := c.NewContainer(ctx, gwclient.NewContainerRequest{
		Platform: want,
	})
	if err != nil {
		t.Fatal(err)
	}
	defer ctr.Release(ctx)

	stored := reflect.ValueOf(ctr).Elem().FieldByName("platform").Elem()
	gotOS := stored.FieldByName("OS").String()
	gotArch := stored.FieldByName("Architecture").String()

	if gotOS != want.OS || gotArch != want.Architecture {
		t.Fatalf(
			"stored platform = %s/%s; want %s/%s",
			gotOS, gotArch, want.OS, want.Architecture,
		)
	}
}

Then run:

bash
go test ./frontend/gateway/forwarder -run '^TestNewContainerForwardsPlatform$' -count=1 -v

Result:

=== RUN   TestNewContainerForwardsPlatform
    newcontainer_platform_test.go:59: stored platform = darwin/arm64; want windows/amd64
--- FAIL: TestNewContainerForwardsPlatform (0.00s)
FAIL

Version information

BuildKit version: v0.32.2 BuildKit commit: 991535e0973488b6a429096d21fa13f81f2d89d8 Daemon platform: linux/arm64 Requested platform: windows/amd64