Skip to content

Multi-Stage Dockerfile

Reference documentation for the operator's multi-stage container build, including OCI image labels, version embedding via ldflags, cross-compilation, and build metadata automation.

Source: Dockerfile, Makefile, internal/version/version.go, .dockerignore

Overview

The operator uses a two-stage Docker build to produce a minimal, secure container image. The builder stage compiles a statically-linked Go binary with embedded version information. The runtime stage copies only the binary into a distroless base image running as a non-root user.

Build metadata (version, git commit, timestamp) flows from make variables through Docker --build-arg flags into Go ldflags, producing a self-describing binary and OCI-labeled image with no manual intervention.


Dockerfile Stages

Builder Stage

PropertyValue
Base imagegolang:1.25
CGO_ENABLED0 (static binary, no libc dependency)
GOOS${TARGETOS:-linux}
GOARCH${TARGETARCH}
ldflags-s -w (strip debug info and DWARF tables)

Build args declared in the builder stage:

ARGPurpose
TARGETOSTarget OS for cross-compilation (default: linux)
TARGETARCHTarget architecture (amd64, arm64)
VERSIONSemantic version injected into the binary
GIT_COMMITGit SHA injected into the binary
BUILD_DATERFC 3339 timestamp injected into the binary

The build command:

dockerfile
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a \
    -ldflags "-s -w \
      -X github.com/c5c3/memcached-operator/internal/version.Version=${VERSION} \
      -X github.com/c5c3/memcached-operator/internal/version.GitCommit=${GIT_COMMIT} \
      -X github.com/c5c3/memcached-operator/internal/version.BuildDate=${BUILD_DATE}" \
    -o manager cmd/main.go

Runtime Stage

PropertyValue
Base imagegcr.io/distroless/static:nonroot
User65532:65532 (nonroot)
Entrypoint/manager
ShellNone (distroless has no shell)
Package managerNone

The runtime stage contains only the compiled manager binary. No build tools, Go runtime, source code, or system utilities are present.


OCI Image Labels

Labels follow the OCI Image Spec annotations. Static labels are hardcoded; dynamic labels use build ARGs that must be re-declared after the FROM instruction (Docker scoping rule).

LabelValueType
org.opencontainers.image.titlememcached-operatorStatic
org.opencontainers.image.descriptionKubernetes operator for managing Memcached clustersStatic
org.opencontainers.image.urlhttps://github.com/c5c3/memcached-operatorStatic
org.opencontainers.image.sourcehttps://github.com/c5c3/memcached-operatorStatic
org.opencontainers.image.licensesApache-2.0Static
org.opencontainers.image.version${VERSION}Dynamic
org.opencontainers.image.revision${GIT_COMMIT}Dynamic
org.opencontainers.image.created${BUILD_DATE}Dynamic

Inspect labels with:

bash
docker inspect --format='{{json .Config.Labels}}' controller:latest | jq .

Version Package

Path: internal/version/version.go

Three package-level variables are set at build time via -X ldflags:

VariableDefaultDescription
VersiondevSemantic version or git describe output
GitCommitunknownFull git commit SHA
BuildDateunknownUTC build timestamp in RFC 3339 format

Functions

String() string — Returns a formatted version string:

text
v1.2.3 (commit: abc1234, built: 2026-02-20T12:00:00Z)

Info() VersionInfo — Returns a VersionInfo struct with all fields accessible:

go
type VersionInfo struct {
    Version   string
    GitCommit string
    BuildDate string
}

Makefile Integration

Build Metadata Variables

Defined at the top of the Makefile with ?= for override-friendly defaults:

VariableDefaultDescription
VERSION$(shell git describe --tags --always --dirty)Version from the latest git tag, falls back to short SHA
GIT_COMMIT$(shell git rev-parse HEAD)Full 40-character commit SHA
BUILD_DATE$(shell date -u +"%Y-%m-%dT%H:%M:%SZ")Current UTC time in RFC 3339

All three commands include 2>/dev/null || echo <fallback> guards for CI environments without a git history or tags.

docker-build

bash
make docker-build IMG=myregistry/memcached-operator:v1.0.0

Passes VERSION, GIT_COMMIT, and BUILD_DATE as --build-arg flags to the container tool (Docker by default, configurable via CONTAINER_TOOL).

docker-buildx

bash
make docker-buildx IMG=myregistry/memcached-operator:v1.0.0

Same build args as docker-build, plus multi-platform support via --platform=linux/arm64,linux/amd64. Builds and pushes in a single step using a dedicated buildx builder instance (memcached-operator-builder).

Overriding Variables

bash
make docker-build VERSION=v2.0.0 GIT_COMMIT=abc1234 BUILD_DATE=2026-01-01T00:00:00Z

Cross-Compilation

The Dockerfile declares TARGETOS and TARGETARCH build args that Docker BuildKit automatically populates during multi-platform builds. The go build command uses these to set GOOS and GOARCH:

PlatformTARGETOSTARGETARCH
Linux AMD64linuxamd64
Linux ARM64linuxarm64

CGO_ENABLED=0 ensures a fully static binary with no libc dependency, which is required for the distroless runtime image (no libc available).


.dockerignore

The .dockerignore file excludes non-essential files from the build context to reduce image build time and prevent leaking sensitive or unnecessary files:

PatternReason
.gitGit history not needed in build
bin/Local build artifacts
testbin/Test binaries
vendor/Vendored dependencies (using module download)
docs/Documentation
cover.outCoverage reports
*.mdMarkdown files
.gitignoreGit configuration
.dockerignoreDocker configuration
.golangci.ymlLinter configuration
.serena/IDE configuration
.planwerk/Planning files
LICENSES/License files
hack/Build scripts
config/Kustomize manifests
test/E2E test fixtures

Only go.mod, go.sum, cmd/, api/, and internal/ are included in the build context.


Security Properties

PropertyImplementation
Non-root executionUSER 65532:65532 in runtime stage
No shell accessdistroless image contains no shell binary
No package managerdistroless image contains no apt/apk
Static binaryCGO_ENABLED=0 eliminates runtime library dependencies
Minimal attack surfaceOnly the manager binary is present in the final image
Stripped binary-s -w ldflags remove symbol tables and debug information