Go

What build metadata does a Go binary carry, and how do you make builds reproducible?

Question 430MediumGo 1.22 to 1.25

Since Go 1.18 every binary embeds its module path, dependency versions and hashes, build settings (GOOS, GOARCH, CGO_ENABLED, -tags, -ldflags), and VCS information (-buildvcs=auto). You can read it at runtime, or from outside with go version -m ./app, which is handy for auditing vulnerable dependencies in deployed binaries.

package main

import (
    "fmt"
    "runtime/debug"
)

func main() {
    bi, ok := debug.ReadBuildInfo()
    if !ok {
        return
    }
    fmt.Println(bi.GoVersion, bi.Main.Path, bi.Main.Version)
    for _, s := range bi.Settings {
        if s.Key == "vcs.revision" || s.Key == "vcs.modified" {
            fmt.Println(s.Key, s.Value)
        }
    }
}

Reproducibility: Go builds are bit-for-bit reproducible (the toolchain itself has been since 1.21) if you:

  • use -trimpath to remove local filesystem paths,
  • pin the toolchain (go and toolchain lines),
  • use CGO_ENABLED=0 or a pinned C toolchain,
  • keep timestamps out of -X values (use the commit time instead).

Gotcha: -buildvcs fails in CI when .git is missing or owned by another user ("error obtaining VCS status"). Use -buildvcs=false or fix safe.directory. Main.Version is (devel) for local builds. Since Go 1.24 it is derived from the VCS tag or pseudo-version.

More on Modules, Packages & Tooling

All 36 Modules, Packages & Tooling questions