Safeguard
Containers

How to Build a Docker Image for Kubernetes Securely

You do not build Docker images inside Kubernetes the old way anymore. Here are the secure patterns for building images that k8s will run, from CI to in-cluster builders.

Yukti Singhal
Platform Engineer
6 min read

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":

  1. 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.
  2. 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 latest cannot 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 USER in 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.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.