The classic way to build an image in a pipeline looks like this: a container with the Docker client connects to the daemon socket on the host, or runs its own daemon in privileged mode. In both cases the CI job receives permissions equivalent to root on the node it runs on. For a pipeline that executes code from a repository many people can push to, that is a risk that cannot be justified.

Where the problem comes from

The Docker daemon has to mount filesystems, create namespaces and configure networking. It needs permissions that inside a container mean the privileged flag or the host’s daemon socket. A compromised CI job, for example through a malicious dependency in the project being built, then gets access to every container on the node, including the secrets of other jobs.

Tools that build without a daemon

Kaniko executes Dockerfile instructions in user space, layer by layer, and pushes the finished image to a registry. It needs no daemon, no privileged and no host access. It runs as an ordinary pod in the cluster. It supports multi-stage builds and layer caching in the registry. Limitations: it does not run containers during the build, so a Dockerfile that requires that has to be restructured; the project is developed more slowly than it used to be, which is worth factoring into plans for the coming years.

Buildah and Podman build images in rootless mode using user namespaces. They offer better Dockerfile compatibility than Kaniko and allow running containers during the build. They do, however, require correctly configured subordinate user IDs on the node or in the executor image, which can be awkward in some clusters.

BuildKit in rootless mode is the engine behind newer Docker versions, run standalone. It has the best support for parallel builds and caching, but its unprivileged setup is the most demanding of the three.

For a small team with a Kubernetes cluster, Kaniko is usually the simplest choice: one executor image, a secret with registry credentials, the context from the repository.

What it looks like in the cluster

The pipeline job starts a pod with the build tool’s image. The pod receives:

  • the build context: the cloned repository as a volume,
  • registry credentials as a secret mounted at the expected configuration path,
  • parameters: the Dockerfile path, the target tag, optionally a cache registry.

The pod has no privileged, mounts no host sockets, and runs in a namespace dedicated to CI with a network policy limited to the registry and the repository. When it finishes, the image is in the registry and the pod is gone.

Rules independent of the tool

  • Tag equals version. An image tagged with a version number or commit hash, never latest. A deployment pointing at latest cannot be rolled back or audited.
  • Base image pinned to a digest. The 3.19 tag of a base image may point to something different tomorrow than today. A digest guarantees repeatability.
  • Base images from your own registry or a proxy. Rate limits and outages of public registries must not stop the pipeline.
  • Scan before publishing. An image with critical vulnerabilities does not receive a production version tag.
  • Layer cache in the registry. Building from scratch on every code change wastes minutes that add up to hours.

Summary

Building without a daemon is not a compromise; it is the removal of permissions the pipeline should never have had. Kaniko for simplicity, Buildah for compatibility, BuildKit for performance; in every case a pod without privileged, tags equal to versions, and base images pinned to digests. A CI job that gets compromised then compromises one job, not the node.