← Back to DevBytes

Kaniko: Complete Configuration Guide

Understanding Kaniko

Kaniko is an open-source tool developed by Google that builds container images from a Dockerfile without requiring a Docker daemon. Unlike traditional Docker builds that rely on a running Docker engine with privileged access to the host filesystem, Kaniko executes the build process entirely in userspace. It works by extracting the filesystem from a base image, then executing each Dockerfile instruction one by one, snapshotting the filesystem after each step, and finally pushing the complete image layers to a container registry.

The name "Kaniko" comes from the Japanese word for "planer" — a woodworking tool that shaves thin layers off wood, which is a fitting metaphor for how Kaniko builds container image layers. Under the hood, Kaniko parses the Dockerfile, resolves multi-stage builds, handles ARG and ENV substitution, and manages the layer cache with precision. It does all this without mounting the host's Docker socket or requiring root privileges on the node, making it exceptionally well-suited for CI/CD pipelines and Kubernetes-native builds.

Core Architecture

Kaniko runs as a single binary inside a container. When you invoke it, the following sequence occurs:

Why Kaniko Matters

🚀 Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

In modern cloud-native development, building container images inside Kubernetes clusters has become a standard requirement. However, the traditional Docker-in-Docker (DinD) approach introduces significant security and operational concerns. Kaniko addresses these pain points directly:

Organizations adopting GitOps workflows, Tekton pipelines, Jenkins X, or GitHub Actions on Kubernetes find Kaniko indispensable. It decouples image building from infrastructure specifics and aligns perfectly with the principle of treating CI/CD systems as cattle, not pets.

Getting Started with Kaniko

The primary way to use Kaniko is by running its official container image as part of your pipeline. The image is available at gcr.io/kaniko-project/executor. You can also download the standalone binary for debugging purposes, but the containerized approach is the recommended production method.

Basic Invocation

The simplest Kaniko command requires specifying a Dockerfile context and a destination registry. Here's the minimal set of flags:

docker run \
  -v $(pwd):/workspace \
  gcr.io/kaniko-project/executor:latest \
  --context=/workspace \
  --dockerfile=/workspace/Dockerfile \
  --destination=myregistry.io/my-image:tag

When running inside a Kubernetes pod, you would define these parameters as container arguments rather than using Docker directly. Let's look at a complete Kubernetes Pod manifest for Kaniko:

apiVersion: v1
kind: Pod
metadata:
  name: kaniko-build
spec:
  containers:
  - name: kaniko
    image: gcr.io/kaniko-project/executor:latest
    args:
    - "--context=git://github.com/myorg/myrepo"
    - "--git-branch=main"
    - "--destination=myregistry.io/my-image:1.0.0"
    - "--cache=true"
    - "--cache-repo=myregistry.io/cache"
    - "--snapshot-mode=time"
    volumeMounts:
    - name: docker-config
      mountPath: /kaniko/.docker
  restartPolicy: Never
  volumes:
  - name: docker-config
    secret:
      secretName: registry-credentials

This Pod definition demonstrates several key features: pulling context from a Git repository, enabling registry-based caching, using timestamp-based snapshots for faster layer detection, and mounting registry credentials from a Kubernetes Secret.

Complete Configuration Reference

Kaniko exposes a rich set of flags that give you fine-grained control over the build process. Understanding these options thoroughly is essential for production deployments where reliability, speed, and security are paramount.

Context Sources

The build context is the set of files available to the Dockerfile. Kaniko supports multiple context sources, each suited to different pipeline architectures:

Here are examples for each context type:

# Local directory context (mounted volume)
executor --context=/workspace --dockerfile=/workspace/Dockerfile

# Git repository context
executor --context=git://github.com/user/repo \
         --git-branch=feature-branch \
         --git-single-branch=true \
         --git-depth=1

# Specific Git commit
executor --context=git://github.com/user/repo \
         --git-commit=abc123def456

# Tar archive context
executor --context=tar:///path/to/context.tar.gz

# GCS bucket context
executor --context=gs://my-bucket/path/to/context.tar.gz \
         --gcs-bucket=my-bucket

# S3 bucket context
executor --context=s3://my-bucket/path/to/context.tar.gz \
         --s3-endpoint=https://s3.amazonaws.com \
         --s3-force-path-style=true

# OCI image context (use an image as the build base files)
executor --context=oci://myregistry.io/base-context-image:latest

Destination Configuration

The destination flag tells Kaniko where to push the final image. You can specify multiple destinations simultaneously, pushing the same image to several registries in one build:

# Single destination
--destination=registry.example.com/project/image:v1.0.0

# Multiple destinations (comma-separated)
--destination=registry1.example.com/image:latest,\
registry2.example.com/image:latest,\
quay.io/org/image:v1.0.0

# Destination with digest verification
--destination=registry.example.com/image:tag \
--push-retry=3 \
--push-retry-delay=500ms

When specifying destinations, always use fully qualified registry URLs. Kaniko supports Docker Hub, Google Container Registry, AWS ECR, Azure Container Registry, Quay.io, Harbor, and any OCI-compliant registry.

Registry Authentication

Authentication is handled through Docker-style config files mounted into the container at /kaniko/.docker/config.json. This file can contain credentials for multiple registries:

{
  "auths": {
    "registry.example.com": {
      "auth": "base64encodedusername:password",
      "email": "user@example.com"
    },
    "gcr.io": {
      "auth": "base64encoded_access_token"
    },
    "public.ecr.aws": {
      "auth": "base64encoded_credentials"
    }
  },
  "credHelpers": {
    "asia.gcr.io": "gcr",
    "eu.gcr.io": "gcr",
    "gcr.io": "gcr",
    "marketplace.gcr.io": "gcr",
    "staging-k8s.gcr.io": "gcr",
    "us.gcr.io": "gcr"
  }
}

To create this secret in Kubernetes from an existing Docker credentials file:

kubectl create secret generic kaniko-auth \
  --from-file=config.json=.docker/config.json \
  --namespace=build-pipeline

For Amazon ECR specifically, you can use the --aws-access-key-id and --aws-secret-access-key flags, or mount AWS credentials:

executor --context=/workspace \
         --destination=123456789.dkr.ecr.us-east-1.amazonaws.com/my-app:latest \
         --aws-access-key-id=AKIAIOSFODNN7EXAMPLE \
         --aws-secret-access-key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY \
         --aws-region=us-east-1

For Google Container Registry, Kaniko can automatically use the application default credentials when running in GKE or on a GCE instance with appropriate scopes.

Snapshot Configuration

Snapshots are how Kaniko determines which files changed after each Dockerfile instruction. The snapshot mode significantly impacts build performance:

# Time-based snapshotting (fastest, default)
--snapshot-mode=time

# Full filesystem hash comparison (most accurate)
--snapshot-mode=full

# Skip snapshotting entirely (use only when you understand implications)
--snapshot-mode=redo

For most workloads, time-based snapshots offer the best balance of speed and correctness. Switch to full mode if you encounter issues with layers not capturing file changes.

Caching Strategies

Kaniko's caching mechanism is one of its most powerful features. Rather than relying on a local Docker layer cache that disappears when the build pod terminates, Kaniko can use a remote registry to persist layer cache:

# Enable registry-based caching
--cache=true
--cache-repo=registry.example.com/build-cache
--cache-copy-layers=true

# Configure cache TTL (default 2 weeks)
--cache-ttl=336h

# Use a specific cache directory locally
--cache-dir=/workspace/cache

# Inline cache (embed cache metadata in the image itself)
--cache=true
--cache-repo=registry.example.com/my-image
--cache-inline=true

The cache workflow operates as follows:

  1. Before each instruction, Kaniko queries the cache repo for a matching layer based on the instruction's key (command string + base image digest)
  2. If found, the cached layer is mounted directly, skipping execution of that instruction
  3. After the build, newly created layers are pushed to the cache repo with appropriate metadata

For multi-stage builds, Kaniko caches each stage independently. This means changing only a final stage's instructions doesn't invalidate the cache for earlier stages.

Build Arguments and Environment Variables

Passing build arguments mirrors Docker's --build-arg behavior exactly:

# Single build argument
--build-arg=VERSION=1.2.3

# Multiple build arguments
--build-arg=BASE_IMAGE=alpine:3.18 \
--build-arg=NODE_VERSION=18 \
--build-arg=NPM_TOKEN=secret

# From a file
--build-arg-file=/workspace/build-args.properties

The build-arg file format is straightforward key-value pairs, one per line:

# build-args.properties
BASE_IMAGE=ubuntu:22.04
PYTHON_VERSION=3.11
APP_ENV=production
FEATURE_FLAG_NEW_UI=true

Environment variables for the build itself (not the resulting image) can be set directly in the pod spec or via the --env-file flag, though this is distinct from Dockerfile ENV instructions which affect the image permanently.

Multi-Stage Build Configuration

Kaniko fully supports multi-stage Dockerfiles. You can target a specific stage, control which stages are cached, and even use the output of one build as the context for another:

# Build only a specific stage
--target=builder

# Build all stages but push only the final
--target=production

# Use a previous stage's image as a base
# (This works automatically in multi-stage Dockerfiles)

Consider this multi-stage Dockerfile and the corresponding Kaniko configuration:

# Dockerfile
FROM golang:1.21 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o server ./cmd/server

FROM alpine:3.18 AS production
RUN apk add --no-cache ca-certificates
COPY --from=builder /app/server /usr/local/bin/server
EXPOSE 8080
CMD ["server"]
# Kaniko build command for the above Dockerfile
executor \
  --context=/workspace \
  --dockerfile=/workspace/Dockerfile \
  --destination=registry.example.com/server:latest \
  --cache=true \
  --cache-repo=registry.example.com/cache \
  --target=production \
  --build-arg=GOARCH=amd64

Kaniko builds both stages, caches the builder stage layers separately from the production stage, and only pushes the final production image. If you change only the source code in the builder stage, the production base layer cache remains valid.

Advanced Feature Flags

Kaniko provides experimental and advanced features controlled via feature flags:

# Enable inline caching metadata
--cache-inline=true

# Skip pushing final image (dry run for validation)
--no-push=true

# Push only to cache, not to destination
--push-only-to-cache=true

# Skip TLS verification (testing only, never in production)
--skip-tls-verify=false

# Enable logging timestamps
--log-timestamp=true

# Set log level
--verbosity=debug

# Custom image ignore list for snapshotting
--ignore-path=/workspace/.git
--ignore-path=/workspace/node_modules

# Custom platform for multi-arch builds
--custom-platform=linux/amd64

# Use a custom kaniko directory
--kaniko-dir=/tmp/kaniko-temp

# Enable reproducible builds
--reproducible=true

For production pipelines, the most relevant flags are --reproducible=true which strips timestamps and ensures byte-for-byte identical layers across builds, and --ignore-path which prevents large directories like .git from being accidentally included in layers.

Volume Mounts and Secrets

Beyond the standard context and Docker config mounts, Kaniko supports additional volume mounts for specialized scenarios:

apiVersion: v1
kind: Pod
metadata:
  name: kaniko-advanced-build
spec:
  containers:
  - name: kaniko
    image: gcr.io/kaniko-project/executor:latest
    args:
    - "--context=/workspace"
    - "--dockerfile=/workspace/Dockerfile"
    - "--destination=registry.example.com/app:latest"
    - "--cache=true"
    - "--cache-repo=registry.example.com/cache"
    - "--cache-dir=/kaniko/cache"
    volumeMounts:
    - name: build-context
      mountPath: /workspace
    - name: docker-config
      mountPath: /kaniko/.docker
    - name: cache-volume
      mountPath: /kaniko/cache
    - name: ssl-certs
      mountPath: /etc/ssl/certs
      readOnly: true
    - name: custom-ca
      mountPath: /kaniko/ssl/certs
  volumes:
  - name: build-context
    persistentVolumeClaim:
      claimName: workspace-pvc
  - name: docker-config
    secret:
      secretName: registry-credentials
  - name: cache-volume
    emptyDir: {}
  - name: ssl-certs
    hostPath:
      path: /etc/ssl/certs
  - name: custom-ca
    configMap:
      name: custom-ca-bundle
  restartPolicy: Never

Kaniko in CI/CD Pipelines

GitHub Actions Integration

Running Kaniko in GitHub Actions is straightforward. Here's a complete workflow that builds an image and pushes it to a registry:

name: Build and Push with Kaniko
on:
  push:
    branches: [main]
    tags: ['v*']
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
    
    - name: Set up Docker config
      run: |
        echo '${{ secrets.REGISTRY_CONFIG }}' > /tmp/config.json
    
    - name: Build with Kaniko
      uses: docker://gcr.io/kaniko-project/executor:latest
      with:
        args: |
          --context=/github/workspace
          --dockerfile=/github/workspace/Dockerfile
          --destination=${{ secrets.REGISTRY }}/${{ github.repository }}:${{ github.ref_name }}
          --cache=true
          --cache-repo=${{ secrets.REGISTRY }}/cache
          --snapshot-mode=time
          --reproducible=true
          --verbosity=info

Tekton Pipeline Task

For Kubernetes-native CI/CD with Tekton, Kaniko integrates as a reusable Task:

apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: kaniko-build
spec:
  params:
  - name: IMAGE
    description: The destination image reference
  - name: DOCKERFILE_PATH
    description: Path to Dockerfile
    default: ./Dockerfile
  - name: CONTEXT_PATH
    description: Build context path
    default: ./
  steps:
  - name: build-and-push
    image: gcr.io/kaniko-project/executor:latest
    args:
    - "--context=$(params.CONTEXT_PATH)"
    - "--dockerfile=$(params.DOCKERFILE_PATH)"
    - "--destination=$(params.IMAGE)"
    - "--cache=true"
    - "--cache-repo=$(params.IMAGE)-cache"
    - "--reproducible=true"
    env:
    - name: AWS_REGION
      valueFrom:
        secretKeyRef:
          name: aws-credentials
          key: region
    volumeMounts:
    - name: docker-config
      mountPath: /kaniko/.docker
  volumes:
  - name: docker-config
    secret:
      secretName: registry-credentials

Jenkins Pipeline (Declarative)

In a Jenkins environment, you can run Kaniko as a container within your pipeline stages:

pipeline {
  agent {
    kubernetes {
      yaml """
        apiVersion: v1
        kind: Pod
        spec:
          containers:
          - name: kaniko
            image: gcr.io/kaniko-project/executor:latest
            command: ['sleep']
            args: ['infinity']
            volumeMounts:
            - name: docker-config
              mountPath: /kaniko/.docker
          volumes:
          - name: docker-config
            secret:
              secretName: registry-auth
        """
    }
  }
  stages {
    stage('Build Image') {
      steps {
        container('kaniko') {
          sh '''
            /kaniko/executor \
              --context=./ \
              --dockerfile=./Dockerfile \
              --destination=registry.example.com/app:${BUILD_ID} \
              --cache=true \
              --cache-repo=registry.example.com/cache \
              --cache-ttl=720h \
              --snapshot-mode=time
          '''
        }
      }
    }
  }
}

Debugging and Troubleshooting

When builds fail, Kaniko provides detailed logs. Enable verbose output and understand common failure modes:

# Enable debug logging
--verbosity=debug

# Show timestamps for performance analysis
--log-timestamp=true

# Save the full build output to a file
--log-format=text

Common Issues and Solutions

Performance Optimization

Building container images efficiently in CI pipelines requires careful tuning. Here are the key optimization levers:

# Optimized production configuration
executor \
  --context=/workspace \
  --dockerfile=/workspace/Dockerfile \
  --destination=registry.example.com/app:latest \
  --cache=true \
  --cache-repo=registry.example.com/cache \
  --cache-copy-layers=true \
  --cache-ttl=168h \
  --snapshot-mode=time \
  --reproducible=true \
  --push-retry=3 \
  --push-retry-delay=1s \
  --ignore-path=/workspace/.git \
  --ignore-path=/workspace/tmp \
  --verbosity=info

Layer Optimization Strategies

Here's an optimized Dockerfile example that works exceptionally well with Kaniko's caching:

FROM node:18-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm \
    npm ci --production

FROM node:18-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM node:18-alpine AS runtime
RUN apk add --no-cache tini curl
COPY --from=builder /app/dist ./dist
COPY --from=deps /app/node_modules ./node_modules
EXPOSE 3000
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["node", "dist/server.js"]

Security Best Practices

Kaniko's architecture inherently improves security compared to Docker-in-Docker, but additional hardening measures are recommended for production environments:

Network Policies for Kaniko Pods

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: kaniko-restricted-egress
spec:
  podSelector:
    matchLabels:
      app: kaniko-build
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          name: kube-system
    ports:
    - port: 53
      protocol: UDP
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
        except:
        - 10.0.0.0/8
        - 172.16.0.0/12
        - 192.168.0.0/16
    ports:
    - port: 443
      protocol: TCP

This network policy restricts Kaniko pods to only communicate with external registries over HTTPS (port 443) and DNS for service discovery, blocking all lateral movement within the cluster.

Multi-Architecture Builds

Kaniko supports building images for multiple CPU architectures. While Kaniko itself runs on a single architecture, you can orchestrate parallel builds for different platforms:

# Build for AMD64
executor \
  --context=/workspace \
  --dockerfile=/workspace/Dockerfile \
  --destination=registry.example.com/app:latest-amd64 \
  --custom-platform=linux/amd64

# Build for ARM64
executor \
  --context=/workspace \
  --dockerfile=/workspace/Dockerfile \
  --destination=registry.example.com/app:latest-arm64 \
  --custom-platform=linux/arm64

After building individual architecture images, use a manifest tool to combine them into a multi-arch manifest list. This is typically done as a separate pipeline step after Kaniko completes:

# Create a multi-arch manifest (separate step, not Kaniko)
docker manifest create registry.example.com/app:latest \
  --amend registry.example.com/app:latest-amd64 \
  --amend registry.example.com/app:latest-arm64

docker manifest push registry.example.com/app:latest

Custom Builds with Kaniko

Using Custom Base Images with Dependencies

Sometimes you need build-time tools that aren't in your base image. Kaniko executes RUN instructions in the context of the current intermediate image, so you can install tools on the fly:

FROM alpine:3.18
RUN apk add --no-cache curl jq
COPY script.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/script.sh
ENTRYPOINT ["/usr/local/bin/script.sh"]

Injecting Secrets at Build Time

For sensitive build-time secrets (API keys, tokens) that should never appear in image layers, use Dockerfile secret mounting (supported by Kaniko):

# Dockerfile with secret mount
FROM alpine:3.18
RUN --mount=type=secret,id=npm_token \
    export NPM_TOKEN=$(cat /run/secrets/npm_token) && \
    npm install --production
COPY . .
RUN npm run build

Mount the secret into the Kaniko container at build time:

apiVersion: v1
kind: Pod
metadata:
  name: kaniko-secret-build
spec:
  containers:
  - name: kaniko
    image: gcr.io/kaniko-project/executor:latest
    args:
    - "--context=/workspace"
    - "--dockerfile=/workspace/Dockerfile"
    - "--destination=registry.example.com/app:latest"
    volumeMounts:
    - name: npm-token-secret
      mountPath: /run/secrets/npm_token
      readOnly: true
  volumes:
  - name: npm-token-secret
    secret:
      secretName: npm-token
      items:
      - key: token
        path: npm_token

The secret is available during the build but does not appear in any committed image layer, preserving security.

Monitoring and Observability

Kaniko emits structured logs that can be integrated into your observability stack. When running in Kubernetes, use a sidecar or your cluster's log aggregation to capture build metrics:

# Key metrics exposed in logs
--log-timestamp=true
--verbosity=info

# Example log output analysis
# Track: context download time, layer pull time,
#         instruction execution time, layer push time,
#         total build duration

For production pipelines, consider wrapping Kaniko with a metrics collector that records build duration, cache hit ratio, and failure rate. These metrics are invaluable for optimizing both your Dockerfiles and your infrastructure.

Conclusion

Kaniko represents a fundamental shift in how container images are built in cloud-native environments. By eliminating the Docker daemon dependency, it enables secure, scalable, and reproducible image builds directly within Kubernetes clusters. Its comprehensive configuration surface — from context sources and caching strategies to snapshot modes and authentication methods — gives developers precise control over every aspect of the build process.

The configuration patterns covered in this guide form the foundation for production-grade image building pipelines. Whether you're implementing a simple CI job with GitHub Actions or orchestrating complex multi-architecture builds across Tekton workflows, Kaniko's flag-driven interface adapts cleanly to your needs. The emphasis on registry-based caching, reproducible builds, and minimal privilege execution aligns with modern software supply chain security requirements.

As container ecosystems continue evolving toward OCI standards and fully declarative build systems, Kaniko's userspace execution model and registry-native caching will only become more relevant. Invest time in understanding its configuration deeply — the payoff manifests in faster builds, tighter security, and pipelines that run reliably regardless of the underlying infrastructure.

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles