Sunday, November 8, 2026

Docker registry for Kubernetes applications. Multi Node and Single node Kubernetes applications

 Yes. In a multi-node Kubernetes cluster, every node that may run your application Pods must be able to reach the container registry URL.

The important detail is that container images are pulled by the container runtime on each Kubernetes node, not by the Pod itself.

For example, suppose you have three nodes:

  • node-1 runs AI Core.

  • node-2 runs API Server.

  • node-3 runs PostgreSQL.

If AI Core and API Server use images stored in registry.example.com:5000, both node-1 and node-2 must be able to resolve and connect to that address. node-3 also needs access if it pulls an image from the same registry.

1. Multi-node registry architecture

You normally need one shared registry, not one registry per Kubernetes node.

2. What must be configured on every node?

Requirement

Explanation

DNS resolution

Each node resolves the registry hostname to a reachable IP

Network connectivity

Each node can connect to the registry's port, normally HTTPS 443

TLS trust

Each node's container runtime trusts the registry certificate

Authentication

Kubernetes supplies image-pull credentials where required

Image reference

Deployments use the same registry hostname and image path

The registry can run on a dedicated VM, a separate Docker host, or inside Kubernetes. For production, an independently hosted registry is often simpler to bootstrap and recover.

3. Configure a shared registry for multiple nodes

Consider this example network:

Component

IP address

Registry server

192.168.1.100

Kubernetes Node 1

192.168.1.101

Kubernetes Node 2

192.168.1.102

Kubernetes Node 3

192.168.1.103

These are illustrative addresses.

Step A — Run the registry on a reachable server

On the registry server, a minimal development example is:

docker run -d \
  --name registry \
  -p 5000:5000 \
  -v registry-data:/var/lib/registry \
  registry:2

Unlike binding to 127.0.0.1, this publishes the registry port on the host's network interfaces. Restrict access with firewall rules.

For production, configure HTTPS with a trusted certificate and registry authentication.

Step B — Verify connectivity from every node

On each Kubernetes node, test:

curl -v http://192.168.1.100:5000/v2/

For an unauthenticated HTTP registry, a successful response is typically {} with HTTP 200.

For an authenticated HTTPS registry, an HTTP 401 response can be expected before authentication.

This establishes network reachability, but doesn't yet guarantee that the container runtime trusts the registry.

Step C — Configure the container runtime

Your Kubernetes nodes use containerd, so it is containerd, not Docker Desktop on your MacBook, that needs to trust the registry.

For a production HTTPS registry using a certificate trusted by the node, normally no insecure-registry configuration is needed.

For an HTTP-only lab registry, containerd needs explicit configuration on each node. For example, with a containerd installation configured to use /etc/containerd/certs.d:

/etc/containerd/certs.d/
  192.168.1.100:5000/
    hosts.toml

Example hosts.toml:

server = "http://192.168.1.100:5000"

[host."http://192.168.1.100:5000"]
  capabilities = ["pull", "resolve"]

The containerd CRI registry configuration must point to that certificates directory, and containerd may need restarting. The exact procedure depends on your containerd version and Kubernetes distribution.

This HTTP configuration is for a trusted development network only.

Step D — Configure Kubernetes authentication

If the registry requires a username and password, create a Kubernetes image pull Secret:

kubectl create secret docker-registry registry-credentials \
  --docker-server=registry.example.com \
  --docker-username=myuser \
  --docker-password='YOUR_PASSWORD' \
  --namespace=myapp

Use the exact registry hostname and port, if applicable, that your image references use.

Then in your Deployment:

spec:
  template:
    spec:
      imagePullSecrets:
        - name: registry-credentials

      containers:
        - name: ai-core
          image: registry.example.com/myapp/ai-core:1.0.0
          imagePullPolicy: IfNotPresent

The Secret is stored in the Kubernetes namespace and is available to the kubelet on whichever node schedules the Pod. You do not need to create a separate Secret on each node.

GitHub+1

4. Configure the registry in Helm

For your application, centralize the registry settings in values.yaml:

global:
  imageRegistry: "registry.example.com"
  imagePullSecrets:
    - name: registry-credentials

aiCore:
  image:
    repository: "myapp/ai-core"
    tag: "1.0.0"
    pullPolicy: IfNotPresent

apiServer:
  image:
    repository: "myapp/api-server"
    tag: "1.0.0"
    pullPolicy: IfNotPresent

Your Helm Deployment template can construct the full image name:

spec:
  template:
    spec:
      imagePullSecrets:
        {{- toYaml .Values.global.imagePullSecrets | nindent 8 }}

      containers:
        - name: ai-core
          image: "{{ .Values.global.imageRegistry }}/{{ .Values.aiCore.image.repository }}:{{ .Values.aiCore.image.tag }}"
          imagePullPolicy: {{ .Values.aiCore.image.pullPolicy }}

Every node uses the same image reference. Kubernetes automatically attempts the pull on the node where the Pod is scheduled.

5. Special consideration for your MacBook and Minikube

Because you're running Minikube with Docker Desktop on macOS, Minikube nodes are containers inside Docker Desktop's Linux environment.

This creates an important networking distinction:

  • localhost:5000 on your MacBook means your MacBook.

  • localhost:5000 inside a Minikube node means that Minikube node.

  • host.docker.internal may provide access to the Docker Desktop host from containers, but its availability and resolution must be verified inside each Minikube node.

For a local experiment, you can use a registry hosted on your MacBook and configure Minikube nodes to reach it through an address available inside Docker Desktop's network. Minikube also documents registry-specific configurations for macOS.

minikube

To test each Minikube node:

minikube node list -p myapp

Then:

minikube ssh -p myapp -n myapp -- \
  curl -v http://host.docker.internal:5000/v2/

Repeat with the other node names reported by minikube node list.

If the hostname doesn't resolve or the connection fails, the registry network must be configured before using it for image pulls.

6. What happens if one node cannot access the registry?

Suppose the registry is reachable from Node 1 and Node 2, but not Node 3.

If Kubernetes schedules AI Core on Node 3:

  1. Kubelet requests the image from containerd.

  2. Containerd attempts to contact the registry.

  3. The connection fails.

  4. The Pod may enter ErrImagePull and subsequently ImagePullBackOff.

Diagnose with:

kubectl describe pod <pod-name> -n myapp

If the required image is already cached on Node 3 and the pull policy is IfNotPresent, Kubernetes may start the Pod without contacting the registry. This is not a reliable substitute for registry connectivity across nodes.

Kubernetes

Recommendation for your environments

For your MacBook multi-node Minikube experiment, use one registry reachable from all Minikube nodes, with the same registry hostname in your Helm values.

For your customer's future multi-node Kubernetes deployment, use a centrally accessible private registry with HTTPS, authentication, and persistent storage. Avoid registry URLs that refer to an individual node's localhost.

The key principle is:

One shared registry → multiple Kubernetes nodes → each node independently pulls the images it needs.

You configure connectivity and TLS trust at the node/container-runtime level, authentication through Kubernetes image pull Secrets, and image names through Helm.

No comments:

Post a Comment