Can't reproduce identical binaries with Go 1.14.2 due to GOPATH

I’m trying to build bit-identical reproducible binaries and failing. I get differences between builds made directly on my local machine and builds made in a Docker container.

Local Build Information

OS: CentOS Linux release 7.7.1908 (Core)

go env:

GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/me/.cache/go-build"
GOENV="/home/me/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/me/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/me/go/src/MyApiServer/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build108965222=/tmp/go-build -gno-record-gcc-switches"

build command:

GOPATH=/home/me/go CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -o MyApiServer -a -ldflags '-s -w -buildid=' MyApiServer/cmd/myserver

Docker Build Information

Image: golang:1.14.2-alpine3.11

go env:

GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/root/.cache/go-build"
GOENV="/root/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build225576264=/tmp/go-build -gno-record-gcc-switches"

Dockerfile build command:

RUN GOPATH=/go CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -o MyApiServer -a -ldflags '-s -w -buildid=' MyApiServer/cmd/myserver

Source Code Information

Modules are enabled:

go.mod

module MyApiServer

go 1.14

require (
	contrib.go.opencensus.io/exporter/jaeger v0.1.0
	contrib.go.opencensus.io/exporter/prometheus v0.1.0
	github.com/alecthomas/participle v0.3.1-0.20190930023849-47e04ebc2ece
	github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1
	github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect
	github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0
	github.com/gomodule/redigo v1.7.0
	github.com/google/uuid v1.1.1
	github.com/mediocregopher/radix/v3 v3.4.0
	github.com/namsral/flag v0.0.0-20170814194028-67f268f20922
	github.com/opentracing/opentracing-go v1.1.0
	github.com/prometheus/client_golang v1.2.0
	github.com/uber-go/atomic v1.3.2 // indirect
	github.com/uber/jaeger-client-go v2.19.0+incompatible
	github.com/uber/jaeger-lib v2.2.0+incompatible // indirect
	go.opencensus.io v0.22.1
	go.uber.org/atomic v1.3.2 // indirect
	golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 // indirect
	gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0
)

I also have a vendor directory MyApiServer/vendor that I have populated via go mod vendor.

What Goes Wrong

My understanding is that with my setup under Go 1.14, Go builds from the vendor directory rather than the module cache. I have verified this by altering a string in a vendor sub-directory and seeing that change propagate to the executable.

I also believe I have set all the flags needed for a binary-reproducible build.

However, the final builds are not bit-for-bit identical. They are the same size, but there are two- and three-byte differences scattered throughout the executable.

Oddly, if I specify a bogus GOPATH for the local build such as this:

GOPATH=/bogus CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -o MyApiServer -a -ldflags '-s -w -buildid=' MyApiServer/cmd/myserver

then I do get an executable that matches that made in Docker. This makes no sense to me. What am I missing?

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.