Build a Go Application
Target audience: Developers Goal: Build a reproducible Go binary using
FROM stagex/pallet-go.
Prerequisites
- Completed the Quick Start tutorial
- Podman installed
- Basic familiarity with Go
Create the Project
We'll build serve — a static file HTTP server using only Go's standard library net/http package.
mkdir serve && cd serve
go.mod
module serve
go 1.26
main.go
package main
import (
"flag"
"log"
"net/http"
)
func main() {
dir := flag.String("d", ".", "directory to serve")
port := flag.String("p", "8080", "port to listen on")
flag.Parse()
log.Printf("Serving %s on :%s", *dir, *port)
log.Fatal(http.ListenAndServe(":"+*port, http.FileServer(http.Dir(*dir))))
}
Write the Containerfile
Create Containerfile:
FROM docker.io/stagex/pallet-go@sha256:4b7f9fe27d84dd9109fe89c4dab1cefdd66bf2f670817ef95ffd335b84fdf2cb AS build
WORKDIR /app
COPY go.mod main.go ./
RUN CGO_ENABLED=0 GO11MODULE=on go build -trimpath -o /serve
FROM scratch
COPY --from=build /serve /serve
ENTRYPOINT ["/serve"]
Key points:
GO11MODULE=on— Enables Go modules. The pallet defaults toGO11MODULE=offfor legacy compatibility, so you must opt in explicitly.CGO_ENABLED=0— Ensures a fully static binary. Go sets this by default whenCGO_ENABLEDis unset, but being explicit is best practice. If you need CGO, usedocker.io/stagex/pallet-cgo@sha256:5feaea6...instead.-trimpath— Removes local filesystem paths from the compiled binary, improving reproducibility.FROM scratch— Go compiles to static binaries. The final image contains only your application — no runtime libraries needed.
Runtime:
FROM scratch— Like Rust, Go compiles to fully static binaries. The final image contains only your application. See the Rust guide for more on theFROM scratchpattern, or the Python guide for a contrast with shared-library runtimes.
Build
podman build --timestamp 1 -t serve .
Output:
[1/2] STEP 1/4: FROM docker.io/stagex/pallet-go@sha256:4b7f9fe...
[1/2] STEP 2/4: WORKDIR /app
[1/2] STEP 3/4: COPY go.mod main.go ./
[1/2] STEP 4/4: RUN CGO_ENABLED=0 GO11MODULE=on go build -trimpath -o /serve
[2/2] COMMIT serve
Run
podman run --rm -d -p 8080:8080 --name serve-test serve -d / -p 8080
curl http://127.0.0.1:8080/
podman kill serve-test
The -d / flag tells serve to serve the root filesystem; -p 8080 sets the port. You should see an HTML directory listing.
Adding External Dependencies
External Go module dependencies follow a two-step pattern: fetch (network allowed) then build (network disabled).
Add a dependency to go.mod, then update the Containerfile:
FROM docker.io/stagex/pallet-go@sha256:4b7f9fe27d84dd9109fe89c4dab1cefdd66bf2f670817ef95ffd335b84fdf2cb AS build
WORKDIR /app
COPY go.mod go.sum* ./
COPY main.go ./
RUN GO11MODULE=on go mod download
RUN --network=none <<EOF
CGO_ENABLED=0 GO11MODULE=on \
go build -trimpath -o /serve
EOF
FROM scratch
COPY --from=build /serve /serve
ENTRYPOINT ["/serve"]
go mod download— Fetches module dependencies from the network. This is the only step that touches the network.go.sum*— Globsgo.sumif it exists (avoids error if missing for new projects).RUN --network=none— The actual compilation is hermetic. All modules were already fetched in the previous step.GOPROXY=offis implicit in--network=none— the build step cannot reach any proxy.
For projects without go.mod (legacy), use go get ./... instead of go mod download.
Verify Reproducibility
The --timestamp 1 flag normalizes filesystem and image metadata timestamps to epoch, ensuring the image digest is deterministic. Rebuild with --no-cache and compare digests:
podman build --no-cache --timestamp 1 -t serve:rebuild .
podman inspect serve --format '{{.Digest}}'
podman inspect serve:rebuild --format '{{.Digest}}'
Both digests will be identical — the pinned toolchain, -trimpath, and CGO_ENABLED=0 guarantee deterministic outputs. See the Python guide for an explanation of why --timestamp 1 is necessary for image reproducibility.
See Also
- Quick Start tutorial — prerequisite
- Build a Rust Application how-to — contrasts Go vs Rust build flags
- Build a Python Application how-to — contrasts static vs shared-library runtime
- Containerfile Syntax reference — syntax details
- Reproduce Builds how-to — verification beyond basic digest check