Kaniko, BuildKit, and Image Volumes: The Evolution of Container Images Inside Kubernetes

Running container images inside Kubernetes is table stakes. Building them there — or mounting their contents as volumes — has been a moving target for years. What started as a privileged hack has evolved into a set of mature, secure, and increasingly native primitives.

This article covers the full arc: why the original approach was broken, what Kaniko solved, why its maintenance status now matters, where BuildKit and Buildah fit, and what Image Volumes (stable in Kubernetes v1.36) change for workflows that don’t need to build anything at all.


The original problem: Docker-in-Docker

The first generation of CI/CD on Kubernetes ran Docker inside Docker. You mounted the host Docker socket (/var/run/docker.sock) into a build container, and that container had full access to the host’s Docker daemon.

# The approach nobody should be using in 2026
volumes:
- name: docker-sock
  hostPath:
    path: /var/run/docker.sock

This worked. It was also a complete security disaster.

Mounting the Docker socket gives the container root-equivalent access to the host. Any workload that can reach that socket can escape the container, inspect other containers, and compromise the node. The only thing standing between your CI pipeline and a full cluster compromise was the good intentions of whoever wrote the build script.

It got worse when the Kubernetes project deprecated Dockershim in v1.20 and removed it in v1.24. Clusters that moved to containerd or CRI-O no longer had a Docker daemon on the host at all. The socket either didn’t exist or belonged to a completely different runtime. Docker-in-Docker in its classic form became functionally impossible on modern clusters.


Kaniko: daemonless builds inside containers

Google released Kaniko in 2018 to solve exactly this problem. Kaniko builds container images entirely in userspace — no daemon, no privileged access, no host socket required.

The key insight: Docker builds work by executing each RUN instruction in a temporary container, snapshotting the filesystem, and saving the result as a layer. Kaniko replicates this logic without a daemon. It runs as a regular container, reads the Dockerfile, executes each step against the local filesystem, and pushes the resulting image directly to a registry.

That design aged well. The project governance did not. The GoogleContainerTools/kaniko repository was archived by its owner on June 3, 2025 and is now read-only. In practical terms, the original Google-hosted Kaniko project should be treated as unmaintained: no active upstream issue triage, no normal pull request flow, and no clear path for security fixes through that repository.

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/your-org/your-repo"
    - "--destination=your-registry/your-image:tag"
    volumeMounts:
    - name: registry-creds
      mountPath: /kaniko/.docker
  volumes:
  - name: registry-creds
    secret:
      secretName: registry-credentials
      items:
      - key: .dockerconfigjson
        path: config.json
  restartPolicy: Never

No host socket. No privileged flag. The container needs write access to its own filesystem (so readOnlyRootFilesystem: true won’t work), but that’s a far narrower requirement than socket mounting.

When Kaniko is still a reasonable answer

Kaniko can still be a reasonable tactical choice when you need to build a container image inside Kubernetes and already have a working, isolated pipeline around it. Existing Kaniko jobs did not stop working when the repository was archived.

For new platform work in 2026, though, do not pick Kaniko by default. The maintenance signal changes the risk model. Use it only if the simplicity is worth owning the upgrade and vulnerability-management story yourself, or if you deliberately standardize on a maintained fork or vendor-supported distribution.

Kaniko integrates well with: – Tekton — the standard pattern is a Tekton Task running the Kaniko executor – Argo Workflows — same pattern, different orchestrator – GitLab CI on Kubernetes — many older examples and pipelines use Kaniko with the Kubernetes executor – Any pod-based CI system — it’s just a container, so it runs anywhere pods run


The alternatives: BuildKit and Buildah

Kaniko is no longer the only serious daemonless option. Two other tools are worth evaluating first for new work:

BuildKit (rootless mode)

BuildKit is the build backend behind docker buildx and the default build system in modern Docker. In rootless mode it runs without privileges and can build images inside a Kubernetes pod.

BuildKit has better caching than Kaniko — particularly layer caching via cache mounts — and supports more advanced Dockerfile features like heredocs and multi-platform builds. The tradeoff is more complex setup: you need to run buildkitd as a sidecar or as a DaemonSet.

For teams already using docker buildx locally, BuildKit is the most natural migration path. Docker’s Kubernetes driver can run BuildKit builders directly in a cluster, including rootless mode without privileged pods on supported Kubernetes versions. The operational cost is real — builder lifecycle, cache persistence, node placement, and rootless kernel requirements — but the project is active and the feature set is where most modern Dockerfile workflows are moving.

Buildah

Buildah is the containers project’s daemonless build tool, designed to integrate with Podman and OCI-native workflows. It can build from Dockerfiles or Containerfiles, and it is the natural choice on OpenShift or in environments where the platform team already standardizes on Red Hat, Podman, and the containers/* stack.

The caveat is that rootless Buildah inside a restricted Kubernetes pod still depends on user namespace behavior and helper binaries such as newuidmap and newgidmap. That is manageable on platforms built for it, especially OpenShift, but it is not automatically a drop-in replacement for every generic Kubernetes CI runner.


Image Volumes: a different problem entirely

Here is where the narrative splits. Everything above is about building images. Image Volumes solve a completely different problem: consuming the contents of an OCI image as a volume, without building anything.

The feature was introduced as alpha in Kubernetes v1.31, moved to beta in v1.33 with subPath and subPathExpr support, became beta enabled by default in v1.35, and graduated to stable (GA) in Kubernetes v1.36.

What it does

Image Volumes let you reference an OCI image in a pod’s volumes section and mount its filesystem contents directly into a container:

apiVersion: v1
kind: Pod
metadata:
  name: image-volume-example
spec:
  containers:
  - name: app
    image: debian
    command: ["sleep", "infinity"]
    volumeMounts:
    - name: config-data
      mountPath: /app/config
  volumes:
  - name: config-data
    image:
      reference: your-registry/your-config-image:v1.2.0
      pullPolicy: IfNotPresent

The container at /app/config sees the contents of your-config-image:v1.2.0. The volume is read-only. No init container required.

From Kubernetes v1.33+, you can also mount a subdirectory with subPath:

volumeMounts:
- name: config-data
  mountPath: /app/config
  subPath: environments/production

The use case: OCI images as artifact bundles

This feature is motivated by a pattern that has been growing quietly: using OCI images not as runnable containers but as versioned, signed, distributable artifact bundles.

The idea: instead of storing configuration, schemas, WASM modules, ML models, or static binaries in a ConfigMap or a separate volume, you package them as an OCI image. You get:

  • Version control — image tags and digests, same tooling you already use
  • Distribution — your existing registry, your existing pull secrets, your existing access controls
  • Signing and attestation — cosign, Sigstore, the full supply chain tooling works on these artifacts
  • Immutability — a digest-pinned image reference is cryptographically immutable

Image Volumes are the Kubernetes primitive that makes this pattern first-class. Without Image Volumes, the workaround was an init container that pulled the image and copied the contents to an emptyDir. It worked, but it was boilerplate.

What it does not do

Image Volumes are not a replacement for Kaniko or any build tool. They consume images; they don’t produce them. If your workflow involves building a new image from source, you still need Kaniko, BuildKit, or equivalent.

They also require container runtime support. CRI-O supported the initial alpha implementation from its v1.31 line and tracked beta support for v1.33; containerd support landed later through the containerd 2.x line. On anything before Kubernetes v1.36, verify both the Kubernetes feature gate and the runtime version before treating this as production plumbing.


Decision framework by Kubernetes version

K8s versionBuild images (CI)Mount image contents as volume
< 1.24Avoid Docker socket builds; use BuildKit, Buildah, or legacy Kaniko with explicit risk acceptanceInit container + emptyDir workaround
1.24 – 1.30BuildKit or Buildah preferred; legacy Kaniko only if already standardizedInit container + emptyDir workaround
1.31 – 1.32BuildKit or Buildah preferred; legacy Kaniko only with maintenance planImage Volumes alpha — ImageVolume feature gate required, runtime support required
1.33 – 1.34BuildKit or Buildah preferred; legacy Kaniko only with maintenance planImage Volumes beta with subPath/subPathExpr, but disabled by default; enable ImageVolume and verify runtime support
1.35BuildKit or Buildah preferred; legacy Kaniko only with maintenance planImage Volumes beta, enabled by default; still verify runtime support before production rollout
1.36+BuildKit or Buildah preferred; legacy Kaniko only for existing pipelines or maintained forksImage Volumes stable (GA), enabled by default

When to use what

Use Kaniko if: – You already have working Kaniko pipelines and the operational cost of migration is higher than the current risk – You have a maintained fork, vendor support, or an internal patching process – You want the simplest daemonless build setup and accept that upstream Google Kaniko is archived

Use BuildKit (rootless) if: – You need advanced cache mounts or multi-platform builds – Your team already uses docker buildx locally – You’re willing to run and operate BuildKit builders in the cluster

Use Buildah if: – You are on OpenShift or a Podman/Red Hat-oriented platform – You want an OCI-native build tool that does not require a daemon – Your cluster policy supports the user namespace requirements of rootless builds

Use Image Volumes if: – You want to inject versioned, signed artifacts into pods without building anything – You’re replacing init container + emptyDir patterns – You’re adopting OCI images as a general artifact format (configs, schemas, binaries)


Conclusion

The container image story inside Kubernetes has matured significantly. Docker-in-Docker is dead — correctly so. Kaniko solved an important build problem in 2018, but the original Google project is archived in 2026, so it should no longer be the default recommendation for new platforms. BuildKit and Buildah are the healthier starting points for active build pipelines, with Kaniko reserved for existing estates or explicitly supported forks.

Image Volumes are genuinely new ground. They’re not competing with Kaniko — they’re addressing a different layer of the same ecosystem: the distribution and consumption of OCI artifacts beyond just “images you run.” With GA in v1.36 and the supply chain tooling around OCI reaching maturity, the pattern of “package it as an image, distribute it like an image, mount it where you need it” is becoming the right answer for a class of problems that previously lived in ConfigMaps, PVCs, or init container hacks.

The right combination depends on what you’re doing and what version you’re running. But for the first time, Kubernetes has native answers for both sides of the equation.

Choose today

If you operate Kubernetes v1.36 or newer, use Image Volumes for read-only artifact injection and choose BuildKit or Buildah for builds. If you are on v1.35, Image Volumes are beta and enabled by default, but you should still verify runtime support and keep the init-container pattern as the rollback path. If you are on v1.33 or v1.34, beta does not mean default-on: enable the ImageVolume feature gate deliberately and validate the runtime. If you are on v1.31 or v1.32, treat Image Volumes as alpha and non-default. If you are below v1.31, Image Volumes are not part of the platform: use emptyDir plus an init container for artifact mounting, and modernize the build path separately.

For CI builds, start with BuildKit when you want Dockerfile compatibility, cache performance, multi-platform output, and a path aligned with docker buildx. Start with Buildah when your cluster is OpenShift or Podman-oriented. Keep Kaniko only where it already works and where someone owns the maintenance risk.

Sources

  • https://github.com/GoogleContainerTools/kaniko
  • https://github.com/kubernetes/enhancements/issues/4639
  • https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/
  • https://kubernetes.io/blog/2024/08/16/kubernetes-1-31-image-volume-source/
  • https://kubernetes.io/blog/2025/04/29/kubernetes-v1-33-image-volume-beta/
  • https://kubernetes.io/docs/tasks/configure-pod-container/image-volumes/
  • https://docs.docker.com/build/builders/drivers/kubernetes/
  • https://github.com/moby/buildkit/blob/master/docs/rootless.md
  • https://github.com/containers/buildah
  • https://github.com/containers/buildah/blob/main/docs/tutorials/05-openshift-rootless-build.md