When people ask how to build a Docker image for Kubernetes, the important answer is that you should not build images the way early tutorials showed, by mounting the Docker socket into a pod, because that hands any build workload root on the node. Kubernetes stopped using Docker as its runtime years ago, and the secure patterns for a Kubernetes build now center on rootless, daemonless builders or, better still, building outside the cluster entirely. This guide covers the practical options and their tradeoffs.
First, separate two questions
There are two distinct things people mean by "kubernetes build docker image":
- Building an image that will run on Kubernetes. Here the build can happen anywhere; the output just has to be an OCI-compliant image in a registry your cluster can pull.
- Building an image from inside a Kubernetes cluster, typically as a CI step running as a pod.
The safest architecture answers the first by doing builds in your CI system outside the cluster and pushing to a registry. You only need in-cluster building when your CI runners themselves live in Kubernetes, and even then the goal is to avoid the dangerous shortcuts.
Why Docker-in-Docker and the socket mount are risky
The old approach was to mount /var/run/docker.sock into a build pod so it could talk to the node's Docker daemon. This is a serious security problem. The Docker daemon runs as root, and access to its socket is effectively root on the host. A malicious or compromised build, or a poisoned base image running arbitrary code during docker build, can escape the pod and control the node, other pods included.
Docker-in-Docker (DinD) with --privileged is only marginally better. It avoids sharing the host daemon but requires a privileged container, which itself grants capabilities that undermine the isolation Kubernetes is supposed to provide. On a modern cluster with Pod Security Admission set to Restricted, a privileged pod will not even be admitted, which is a good hint that the pattern is discouraged.
The Kubernetes docker image build story also changed because Kubernetes removed the dockershim: the cluster runtime is now containerd or CRI-O, and there is no Docker daemon on the node to talk to anyway. The socket-mount approach is both unsafe and increasingly unavailable.
The secure in-cluster options
If you must build inside Kubernetes, use a daemonless, rootless builder:
Kaniko builds an image from a Dockerfile entirely in userspace, with no Docker daemon and no privileged mode. It executes each Dockerfile instruction and snapshots the filesystem in the container, then pushes to a registry. A typical build pod:
apiVersion: v1
kind: Pod
metadata:
name: kaniko-build
spec:
containers:
- name: kaniko
image: gcr.io/kaniko-project/executor:latest
args:
- "--dockerfile=Dockerfile"
- "--context=git://github.com/org/repo.git"
- "--destination=registry.example.com/app:1.4.0"
volumeMounts:
- name: docker-config
mountPath: /kaniko/.docker
restartPolicy: Never
volumes:
- name: docker-config
secret:
secretName: registry-credentials
BuildKit (the engine behind modern docker build) can run as a rootless pod and is the most capable option, with strong caching and multi-platform support. buildah is another daemonless choice, common in Red Hat and OpenShift environments.
All three avoid the privileged/root requirement, which means they play nicely with a Restricted Pod Security Standard and do not give a build the keys to the node.
Harden the build itself
Choosing a safe builder is only half the job. The image you produce is a supply chain artifact, and the build is where its trustworthiness is decided.
- Use minimal, pinned base images. Prefer distroless or slim bases and pin by digest, not a floating tag, so
latestcannot shift under you. - Multi-stage builds. Compile in a builder stage and copy only the artifacts into a minimal runtime stage. This keeps compilers, package managers, and build secrets out of the shipped image.
- Never bake secrets into layers. A secret added in one layer and deleted in the next is still recoverable from image history. Use build secrets mounted at build time (BuildKit's
--secret) instead. - Run as non-root. Set a
USERin the Dockerfile so the resulting container does not default to root, which also keeps it compatible with a Restricted pod profile. - Scan before you push. Run an image scan in the pipeline so a base image with a known critical CVE fails the build. An SCA and container scanning tool fits here and can also produce an SBOM of exactly what went into the image.
A recommended pipeline
For most teams the cleanest pattern is: build in CI (GitHub Actions, GitLab CI, or similar) using BuildKit, scan the image and generate an SBOM, sign it, push to a registry, and let Kubernetes pull the signed, scanned image at deploy time. Enforce at admission that only signed images from your registry can run, using a policy engine or an admission controller. This keeps the privileged build work off your cluster nodes entirely and gives you a verifiable chain from source to running pod.
If your runners do live in the cluster, swap the CI builder for Kaniko or rootless BuildKit and keep everything else the same. Our academy covers building that source-to-deploy chain end to end, including image signing and admission policy.
FAQ
Can you build a Docker image inside Kubernetes?
Yes, but not by mounting the Docker socket or using privileged Docker-in-Docker. Use a rootless, daemonless builder such as Kaniko, rootless BuildKit, or buildah, which build images without needing root on the node.
Why is mounting the Docker socket dangerous?
The Docker daemon runs as root, so access to its socket is effectively root on the host node. A compromised build can escape the pod and control the node and every pod on it. Modern clusters also have no Docker daemon after the dockershim removal.
Do I need to build images inside the cluster at all?
Usually not. Building in CI outside the cluster and pushing to a registry is the safest and simplest pattern. Only build in-cluster when your CI runners themselves run in Kubernetes.
How do I keep build secrets out of the image?
Never add secrets in a layer, even if you delete them later, because image history retains them. Use build-time secret mounts such as BuildKit's --secret, and use multi-stage builds so build-time material never reaches the final runtime image.