Kubernetes
kind
Docker
container images
DevOps

kind cluster - how to see docker-images that are loaded?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Kind (Kubernetes IN Docker) runs Kubernetes clusters using Docker containers as nodes. Since Kind nodes are Docker containers themselves, images loaded into a Kind cluster are not visible with docker images on the host. To see images inside a Kind cluster, use docker exec to run crictl images inside the node container, or use kind load docker-image to load local images and then verify with crictl. Understanding this separation between the host Docker daemon and the Kind node's container runtime is key to debugging image pull issues.

Loading Images into Kind

Kind nodes use containerd as their container runtime, not the host's Docker daemon. You must explicitly load images from the host into the cluster:

bash
1# Build an image locally
2docker build -t my-app:latest .
3
4# Load it into the Kind cluster
5kind load docker-image my-app:latest
6
7# Load into a specific cluster (if you have multiple)
8kind load docker-image my-app:latest --name my-cluster
9
10# Load multiple images
11kind load docker-image my-app:latest my-sidecar:v1 nginx:1.25

After loading, the image is available inside the Kind node's containerd storage, and Kubernetes can pull it with imagePullPolicy: Never or imagePullPolicy: IfNotPresent.

Viewing Images Inside a Kind Cluster

bash
1# Find the Kind node container name
2docker ps
3# CONTAINER ID   IMAGE                  NAMES
4# abc123         kindest/node:v1.29.0   my-cluster-control-plane
5
6# List images inside the Kind node
7docker exec -it my-cluster-control-plane crictl images
8
9# Output:
10# IMAGE                          TAG       IMAGE ID       SIZE
11# docker.io/library/my-app       latest    abc123def456   45.2MB
12# registry.k8s.io/coredns        v1.11.1   ...            16.2MB
13# registry.k8s.io/etcd           3.5.10    ...            103MB
14# registry.k8s.io/kube-apiserver v1.29.0   ...            120MB

Method 2: ctr (containerd CLI)

bash
1# List all images in containerd's namespace
2docker exec -it my-cluster-control-plane ctr --namespace k8s.io images list
3
4# Filter by name
5docker exec -it my-cluster-control-plane ctr --namespace k8s.io images list | grep my-app

Method 3: kubectl (Indirect)

bash
1# Check what images are actually used by running pods
2kubectl get pods -A -o jsonpath='{range .items[*]}{.spec.containers[*].image}{"\n"}{end}' | sort -u
3
4# Describe a node to see images cached on it
5kubectl describe node my-cluster-control-plane | grep -A 50 "Non-terminated Pods"

Loading from a tar Archive

If you have images saved as tar files:

bash
1# Save a Docker image to tar
2docker save my-app:latest -o my-app.tar
3
4# Load tar into Kind cluster
5kind load image-archive my-app.tar
6
7# Verify it was loaded
8docker exec -it my-cluster-control-plane crictl images | grep my-app

This is useful in CI/CD pipelines where images are built in one step and loaded in another.

Kubernetes Deployment with Local Images

When using locally loaded images, set imagePullPolicy to avoid pulling from a remote registry:

yaml
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4  name: my-app
5spec:
6  replicas: 1
7  selector:
8    matchLabels:
9      app: my-app
10  template:
11    metadata:
12      labels:
13        app: my-app
14    spec:
15      containers:
16      - name: my-app
17        image: my-app:latest
18        imagePullPolicy: Never  # Use the locally loaded image
19        ports:
20        - containerPort: 8080

If you omit imagePullPolicy: Never, Kubernetes defaults to Always for :latest tags, causing ErrImagePull since the image is not in any remote registry.

Using a Local Registry Instead

For development workflows with frequent image changes, a local registry avoids repeated kind load:

bash
1# Create a local registry container
2docker run -d --restart=always -p 5001:5000 --name kind-registry registry:2
3
4# Create a Kind cluster connected to the registry
5cat <<EOF | kind create cluster --config=-
6kind: Cluster
7apiVersion: kind.x-k8s.io/v1alpha4
8containerdConfigPatches:
9- |-
10  [plugins."io.containerd.grpc.v1.cri".registry.mirrors."localhost:5001"]
11    endpoint = ["http://kind-registry:5001"]
12EOF
13
14# Connect the registry to the Kind network
15docker network connect kind kind-registry
16
17# Push images to the local registry
18docker tag my-app:latest localhost:5001/my-app:latest
19docker push localhost:5001/my-app:latest
20
21# Use in Kubernetes
22# image: localhost:5001/my-app:latest

Multi-Node Clusters

In multi-node Kind clusters, kind load docker-image loads the image into all nodes:

bash
1# Create a multi-node cluster
2cat <<EOF | kind create cluster --config=-
3kind: Cluster
4apiVersion: kind.x-k8s.io/v1alpha4
5nodes:
6- role: control-plane
7- role: worker
8- role: worker
9EOF
10
11# Load image — goes to all nodes
12kind load docker-image my-app:latest
13
14# Verify on each node
15docker exec -it my-cluster-control-plane crictl images | grep my-app
16docker exec -it my-cluster-worker crictl images | grep my-app
17docker exec -it my-cluster-worker2 crictl images | grep my-app

Common Pitfalls

  • Running docker images on the host to find Kind images: Images loaded into Kind are stored inside the node container's containerd, not the host Docker daemon. Use docker exec <node> crictl images to list them.
  • Forgetting imagePullPolicy: Never for local images: Kubernetes defaults to imagePullPolicy: Always for :latest tags. Without Never or IfNotPresent, Kubernetes tries to pull from a remote registry and fails with ErrImagePull.
  • Loading images before the cluster is created: kind load docker-image requires a running cluster. Create the cluster with kind create cluster first, then load images.
  • Image name mismatch between load and deployment: The image name in kind load docker-image my-app:v1 must exactly match the image: field in the Kubernetes manifest, including the tag. A mismatch causes ImagePullBackOff.
  • Not rebuilding before reloading: kind load docker-image loads the current image from the host Docker daemon. If you change code, you must docker build again before running kind load — it does not automatically rebuild.

Summary

  • Use kind load docker-image <name> to load local Docker images into a Kind cluster
  • View loaded images with docker exec -it <node-name> crictl images
  • Set imagePullPolicy: Never in Kubernetes manifests for locally loaded images
  • Use kind load image-archive for tar-based image loading in CI/CD pipelines
  • For frequent development cycles, set up a local Docker registry connected to the Kind network

Course illustration
Course illustration