Kaniko: building container images without a Docker daemon
Kaniko builds and pushes container images without a Docker daemon, and the setup for it differs across GitHub Actions, Google Cloud Build, and CircleCI.
· 2 min read
Kaniko builds a container image from a Dockerfile and pushes it to a registry without needing a Docker daemon anywhere in the process. That matters specifically in Kubernetes-based CI, where running a privileged Docker-in-Docker sidecar just to build an image is either disabled by policy or a genuine security liability. Kaniko runs entirely in userspace as a normal, unprivileged pod.
GitHub Actions
name: Build and Push Image
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Build and Push Image with Kaniko
run: |
mkdir -p /kaniko/.docker
echo "{\"auths\":{\"<registry_url>\":{\"username\":\"$DOCKER_USERNAME\",\"password\":\"$DOCKER_PASSWORD\"}}}" > /kaniko/.docker/config.json
/kaniko/executor \
--dockerfile=Dockerfile \
--context=. \
--destination=<registry_url>/<image_name>:latest
env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
Registry credentials come from GitHub secrets, written to /kaniko/.docker/config.json before the executor runs, since Kaniko reads Docker’s standard config format for auth rather than taking credentials as flags.
Google Cloud Build
Cloud Build runs Kaniko’s executor image directly as a build step, with the build context pulled from a storage bucket and credentials handled through Secret Manager:
steps:
- name: 'gcr.io/kaniko-project/executor:latest'
args:
- "--dockerfile=Dockerfile"
- "--context=gs://<your-bucket-name>/<path-to-context>/"
- "--destination=gcr.io/<your-project-id>/<image-name>:latest"
env:
- "GOOGLE_APPLICATION_CREDENTIALS=/secret/gcp-key.json"
availableSecrets:
secretManager:
- versionName: "projects/<your-project-id>/secrets/<secret-name>/versions/latest"
mountPath: "/secret/"
CircleCI
CircleCI runs the Kaniko executor image as the job’s own container, rather than invoking it as a separate build step, so credentials are assembled the same way as the GitHub Actions example, from environment variables set in the CircleCI project:
version: 2.1
jobs:
build:
docker:
- image: gcr.io/kaniko-project/executor:latest
steps:
- checkout
- run:
name: Build and Push Image with Kaniko
command: |
mkdir -p /kaniko/.docker
echo "{\"auths\":{\"$REGISTRY_URL\":{\"username\":\"$DOCKER_USERNAME\",\"password\":\"$DOCKER_PASSWORD\"}}}" > /kaniko/.docker/config.json
/kaniko/executor \
--dockerfile=Dockerfile \
--context=. \
--destination=$REGISTRY_URL/$IMAGE_NAME:latest
workflows:
version: 2
build-and-push:
jobs:
- build
The pattern is the same everywhere: write registry credentials to Kaniko’s expected config path, then point the executor at a Dockerfile, a context, and a destination. What changes between platforms is how the credentials and the build context get to the executor in the first place, not how Kaniko itself works.