make run command is convenient for local development, but to deploy the operator to another Kubernetes cluster you must publish a container image that cluster nodes can pull and run.
Kubebuilder has already scaffolded the important pieces for building that image: a Dockerfile, Makefile targets, and Kubernetes manifests under config/. This document inspects the Dockerfile and Makefile pieces that go into the operator image. Building and pushing the image will be covered later in an end-to-end flow.
References
Dockerfile overview
The repository Dockerfile uses a multi-stage build pattern:- Stage 1 (builder): compiles the Go-based operator manager binary using an official Go image.
- Stage 2 (runtime): packages only the compiled binary into a minimal runtime image (distroless).
First stage — builder (compiles the manager binary)
- Copying
go.modandgo.sumbefore the rest of the source enables Docker to cache dependencies. Changing application source code afterwards won’t force a re-download of modules. CGO_ENABLED=0produces a statically-linked binary, required when using distroless base images that lack C libraries.TARGETOSandTARGETARCHare build arguments that let Buildx inject platform values for multi-architecture images.
Second stage — runtime (minimal image containing only the binary)
- The final image contains only the compiled
managerbinary — no package managers, shells, or interpreters — reducing image size and attack surface. - Running as a non-root user increases runtime security.
- Distroless images are ideal for production but limit in-container debugging because typical tooling is absent.
Using a distroless image with a static binary improves security and minimizes image size, but it also means debugging inside the container is limited since shells and typical tooling are absent.
Makefile targets for building and publishing
The Makefile in the project exposes basic single-architecture build/push targets and a Buildx-based cross-platform workflow for producing multi-architecture images.Simple single-architecture build and push
linux/amd64). Do not run these if you intend to publish multi-architecture images — use the Buildx workflow instead.
Cross-platform build and push (buildx)
PLATFORMSenumerates the CPU architectures to include in the image manifest list served by the registry.- The
sedcommand writes a temporaryDockerfile.crossthat forces BuildKit to respect theBUILDPLATFORMon eachFROMinstruction. This is critical so that the correct base image for each target architecture is used during the multi-arch build. buildx build --push --platform=...builds images for multiple architectures and pushes a single tag that points to a manifest list (multi-arch image). The registry then serves the appropriate architecture image for each node automatically.
If a tag only points to an
amd64 image, an arm64 node cannot run that image. Use a multi-arch manifest list (via Buildx) to support heterogeneous clusters.Separating image and install manifests
It’s important to keep the container image build pipeline distinct from Kubernetes installation manifests:- Image: contains only the controller binary and runtime configuration (Dockerfile, build process).
- Install manifests: CRDs, RBAC, Deployment, Service, and sample manifests are under
config/and are generated or applied by other Make targets.
What’s next
This article toured the Dockerfile and Makefile pieces used to produce a minimal, secure operator image and explained how to produce a multi-architecture manifest list using Buildx. Later you can perform the end-to-end build and push flow:- Choose an image tag and registry (for example,
docker.io/<your-org>/webapp-operator:v0.1.0). - Run the
docker-buildxtarget to publish a multi-arch image. - Update your operator Deployment manifests under
config/to reference the published image tag.
- Kubebuilder documentation
- Distroless images (GitHub)
- Docker Buildx documentation
- Container image best practices (Google)