Xeonr Developer Docs
Runtimes

Go

Anything with a go.mod. Built with your module's toolchain and shipped on distroless.

A Go function is any source tree with a go.mod at its root.

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {
        fmt.Fprintln(w, "hello")
    })
    http.ListenAndServe(":"+os.Getenv("PORT"), nil)
}

Which package is built

Two conventions cover almost every repository:

  1. main.go at the root → the root package is built.
  2. Exactly one directory under cmd/ → that one is built.

Anything else is genuinely ambiguous, so it asks rather than guesses. If cmd/ holds several commands, the deploy fails naming them and you set an explicit main package in settings (for example ./cmd/api). Guessing here would produce a build that succeeds and runs the wrong binary.

Toolchain version

Taken from the go directive in go.mod, reduced to major.minor because that is what the toolchain image tags track. An unparseable or missing directive falls back to Go 1.26 rather than failing the deploy.

The build

Multi-stage, always:

golang:<version>-bookworm        gcr.io/distroless/static-debian12:nonroot
├── COPY go.mod go.sum           └── COPY --from=build /out/app /app
├── go mod download
├── COPY .
└── go build -trimpath -ldflags="-s -w" -o /out/app <main package>

CGO_ENABLED=0, so the binary is static and the runtime image needs no libc.

Multi-stage is not a nicety. A binary shipped inside golang:1.26 is a ~1 GB image, and image pull is on the cold-start path — which is the product.

The runtime image

Base (no packages declared)gcr.io/distroless/static-debian12:nonroot
Base (packages declared)debian:stable-slim
Binary/app
EnvironmentPORT set to the configured port (8080 by default)

Distroless has no shell and no package manager, which is both the smallest attack surface and the fastest pull. Declaring system packages switches the runtime to Debian, because distroless has no apt — so declare them only when you actually need them.

Setting an explicit entrypoint in settings overrides /app.

On this page