iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

go build / install

`go build` compiles Go source into a single static binary. The default build is fine for most cases; the interesting flags are -ldflags (version stamping), -trimpath (reproducible builds), -tags (conditional compilation), CGO_ENABLED=0 (truly static), and GOOS/GOARCH (cross-compile). Together they cover what a build server needs.

Versioned, reproducible, cross-compiled binaries

EXAMPLE
# 1) Plain build — outputs a binary named after the module
go build ./cmd/api
./api

# 2) Output path, install path
go build -o bin/api ./cmd/api
go install ./cmd/api               # places binary in $GOBIN (usually ~/go/bin)

# 3) Cross-compile — same source, different OS/arch
GOOS=linux  GOARCH=amd64 go build -o bin/api-linux-amd64  ./cmd/api
GOOS=darwin GOARCH=arm64 go build -o bin/api-darwin-arm64 ./cmd/api
GOOS=windows GOARCH=amd64 go build -o bin/api.exe         ./cmd/api

# Supported targets:
go tool dist list | head -10

# 4) Static binary (no libc dependency, fits the FROM scratch Docker pattern)
CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags="-s -w" -o bin/api ./cmd/api
# -trimpath  : strip absolute paths from binary (reproducible builds)
# -ldflags="-s -w" : strip debug info -> smaller binary

# 5) Embed version info via -ldflags -X
# main.go: var version = "dev"
VERSION=$(git describe --tags --always)
COMMIT=$(git rev-parse --short HEAD)
DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)

go build -trimpath \
  -ldflags "-s -w \
    -X main.version=${VERSION} \
    -X main.commit=${COMMIT} \
    -X main.date=${DATE}" \
  -o bin/api ./cmd/api

# Then inside the binary: log.Printf("version=%s commit=%s built=%s", version, commit, date)

# 6) Build tags — conditional compilation
# code: //go:build linux
# build only on Linux:
go build -tags "linux,production" ./...
# Files with //go:build prometheus are included only with -tags prometheus

# 7) Race detector — invaluable for CI
go build -race -o bin/api-race ./cmd/api      # MUST keep CGO_ENABLED for race
go test -race ./...

# 8) PGO — Profile-Guided Optimization (Go 1.21+)
go test -cpuprofile=default.pgo -run=BenchmarkX ./...
go build -pgo=default.pgo ./cmd/api           # 2-7% perf gains on hot paths

# 9) Reproducible builds checklist
# - -trimpath
# - go env -w GOFLAGS=-mod=readonly
# - GOPROXY (pin), GOSUMDB on
# - same Go version across builders (specify in go.mod via the "go 1.22" directive)
# - SOURCE_DATE_EPOCH set if the build embeds timestamps

# 10) Tiny Docker image
# Dockerfile
# FROM golang:1.22-alpine AS build
# WORKDIR /src
# COPY go.mod go.sum ./
# RUN go mod download
# COPY . .
# RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/api ./cmd/api
#
# FROM scratch
# COPY --from=build /out/api /api
# ENTRYPOINT ["/api"]

Why it matters

Combine -trimpath + -ldflags -s -w + CGO_ENABLED=0 for production builds and FROM scratch in Docker. The resulting image is typically 10–20 MB instead of 800, has no shared libraries to keep patched, and starts in milliseconds — the operational gains compound across every host the binary lands on.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
go build ./...
go install ./cmd/myapp
GOOS=linux GOARCH=amd64 go build -o app .
Try it Yourself »

Discussion

Loading…