Safeguard
Developer Tools•

How should a Go Dockerfile handle modules and go mod verify?

Copy go.mod and go.sum first, run go mod download and go mod verify in their own layer, then build. Faster rebuilds and a tamper check on cached modules.

Safeguard Security Team
4 min read

Copy go.mod and go.sum into the build stage first, run go mod download and go mod verify in their own layer, then copy the source and build. The dependency layer is cached until the module files change, and verify checks that downloaded modules match their recorded checksums.

The pattern

FROM golang:1.25 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download && go mod verify
COPY . .
RUN CGO_ENABLED=0 go build -trimpath -o /out/app ./cmd/app

FROM gcr.io/distroless/static-debian13:nonroot
COPY --from=build /out/app /app
CMD ["/app"]

Use the Go version your project requires, and pin the build image more tightly than a bare tag in real use.

What each command is documented to do

The Go command documentation describes go mod download as downloading the named modules, and with no arguments, the modules needed to build and test the main module. It adds that the go command downloads modules automatically during ordinary execution, and that the download command is useful mainly for pre-filling the local cache.

That is exactly the Docker use: pre-fill the cache in a layer that only changes when go.mod or go.sum change.

The documentation describes go mod verify as checking that the dependencies of the current module, stored in the local downloaded source cache, have not been modified since being downloaded. If all modules are unmodified, it prints all modules verified. Otherwise it reports which modules changed and exits with a non-zero status, which fails the Docker build.

Why go.sum matters

The Go project's module guidance says that when you add dependencies, Go tools create a go.sum file containing checksums of the modules you depend on. Go uses this to verify the integrity of downloaded module files. The guidance also says to include go.mod and go.sum in your repository with your code.

In a container build, that means a missing or stale go.sum is a signal to fix, not to work around. A build that regenerates go.sum inside the image silently accepts whatever it downloads.

Settings worth knowing

SettingWhat the Go docs say it doesDocker use
GOPROXYURL of the module proxyPoint at an internal mirror if you run one
GOSUMDBName of the checksum databaseLeave the default unless you have a reason
mod flag set to readonlyDo not let the build update go.modCatch drift in CI
mod flag set to vendorBuild from a vendor directoryOffline or hermetic builds

The build flag documentation notes that if a vendor directory is present and go.mod declares Go 1.14 or higher, the go command acts as if the vendor mode were set. That matters if someone commits a vendor directory and forgets to refresh it.

Pitfalls

  • Copying the whole source before go mod download defeats the cache, since any code change invalidates the layer.
  • Private modules need credentials. Do not bake tokens into a layer, use a build secret mount.
  • A verify step proves the cache matches recorded checksums, not that the modules are safe. A malicious version with a valid checksum passes. That is a job for dependency scanning.

Where Safeguard fits

Safeguard reads go.mod and go.sum to scan Go dependencies and produce an SBOM, and policies can stop a pull request that introduces a blocked module. Try the free scan, or see how a gate behaves in the demo below.

Try it yourself

Open the demo, trigger the gate on a pull request and see how a new dependency is evaluated before merge.

Click through it at your own pace, or open it full screen.

Quick check

Does go mod verify detect a malicious dependency?

No. It detects modification of a module after download compared with recorded checksums. A module that was malicious when first published still verifies.

Should I commit a vendor directory for Docker builds?

It is an option for hermetic builds. If you do, keep it in sync with go.mod, because the go command uses it by default under the conditions in the documentation.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.