Local Registry Guide¶
This guide explains how to use kindplane's built-in local container registry for faster development iteration.
Overview¶
When developing with Kind, you often need to test locally built container images. By default, this requires pushing images to a remote registry, which can be slow. kindplane's local registry feature solves this by creating a local Docker registry that Kind nodes can pull from directly.
Enabling the Local Registry¶
Add the registry configuration to your kindplane.yaml:
Configuration Options¶
| Option | Type | Default | Description |
|---|---|---|---|
enabled | bool | false | Enable local container registry |
port | int | 5001 | Host port for the registry |
persistent | bool | false | Keep registry container after kindplane down |
name | string | kind-registry | Registry container name |
Persistent Mode¶
By default, the registry container is removed when you run kindplane down. To preserve images across cluster recreations, enable persistent mode:
This is useful when:
- You have large images that take time to build
- You want to preserve images between development sessions
- You're testing multiple cluster configurations
Usage Workflow¶
1. Create the Cluster¶
This creates both the Kind cluster and the local registry.
2. Build and Tag Your Image¶
Tag your image to use the local registry:
# Build your image
docker build -t my-app:latest .
# Tag for the local registry
docker tag my-app:latest localhost:5001/my-app:latest
Or build directly with the registry tag:
3. Push to the Registry¶
4. Use in Kubernetes¶
Reference the image in your Kubernetes manifests:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: localhost:5001/my-app:latest
imagePullPolicy: Always
Or use kubectl directly:
How It Works¶
When you enable the local registry, kindplane:
-
Creates a registry container - Runs the
registry:2Docker image bound tolocalhost:{port} -
Configures containerd - Patches the Kind node's containerd configuration to recognise the registry
-
Sets up hosts.toml - Creates registry configuration on each Kind node to map
localhost:{port}to the registry container -
Connects to Kind network - Ensures the registry container is on the same Docker network as Kind nodes
-
Creates a ConfigMap - Adds
local-registry-hostingConfigMap tokube-publicnamespace for discovery
Integration with Image Caching¶
When both local registry and image caching are enabled, kindplane automatically optimizes your workflow:
- Checks for images in your local Docker daemon
- Tags and pushes them to the local registry (
localhost:5001) - Kubernetes pulls from the local registry (fast, cached)
This provides the fastest bootstrap experience:
- No remote registry pulls for cached images
- Registry acts as shared cache for all nodes
- Images persist if registry is in persistent mode
Example Workflow¶
# kindplane.yaml
cluster:
name: kindplane-dev
registry:
enabled: true
persistent: true
crossplane:
version: "1.15.0"
# imageCache is enabled by default
# Pre-pull images once
docker pull crossplane/crossplane:v1.15.0
docker pull xpkg.upbound.io/crossplane-contrib/provider-kubernetes-controller:v0.12.0
# Bootstrap - images are automatically pushed to local registry
kindplane up
# Verify images in registry
curl http://localhost:5001/v2/_catalog
# {"repositories":["crossplane/crossplane","crossplane-contrib/provider-kubernetes-controller"]}
# Fast iterations - images stay in persistent registry
kindplane down
kindplane up # Very fast! No pulls needed
Performance Boost
Combining local registry with image caching provides: - First bootstrap: ~30-60s faster (vs. remote pulls) - Subsequent bootstraps: ~60-120s faster (persistent registry cache)
Accessing from Pods¶
There's an important distinction between host access and in-cluster access:
| Context | Address | Protocol |
|---|---|---|
| Host machine | localhost:5001 | HTTP |
| Pod manifests | localhost:5001 | HTTP (via containerd mapping) |
| Inside pods (code) | kind-registry:5000 | HTTP |
Why Different Addresses?
localhost inside a container refers to the container's own network namespace, not the host. The containerd configuration maps localhost:5001 to the registry for image pulls, but code running inside pods must use the registry's Docker network name.
Troubleshooting¶
Check Registry Status¶
# Check if registry container is running
docker ps | grep kind-registry
# Check registry connectivity
curl http://localhost:5001/v2/_catalog
Check Node Configuration¶
# List Kind nodes
docker exec kind-control-plane cat /etc/containerd/certs.d/localhost:5001/hosts.toml
Check ConfigMap¶
Image Pull Errors¶
If pods fail to pull images:
-
Verify the image was pushed successfully:
-
Check the image reference matches exactly (including tag)
-
Ensure
imagePullPolicy: AlwaysorimagePullPolicy: IfNotPresentis set appropriately
Registry Container Missing¶
If the registry container was accidentally removed:
Or manually create it:
docker run -d --restart=always -p 127.0.0.1:5001:5000 --network bridge --name kind-registry registry:2
docker network connect kind kind-registry
Best Practices¶
-
Use consistent tagging - Always include version tags, avoid using just
latestin production -
Enable persistent mode - If you're iterating on images frequently, enable
persistent: trueto avoid re-pushing after cluster recreation -
Clean up old images - The registry can grow large over time. Consider periodically cleaning up:
-
Use in CI/CD - The local registry is ideal for CI pipelines that need to test images in Kubernetes without pushing to external registries