In my previous post I used declarative configuration to deploy a sample app to Kubernetes. The declarative configuration used a YAML (Yet Another Markup Language) manifest.

That app used a simple image I had built locally. I therefore used this line within my manifest to ensure the image would not be pulled when the pod was created:

imagePullPolicy: Never

Most applications will use a private image. And of course that image will need to be stored in a repository that’s accessible to remote staging/production clusters. The imagePullPolicy: Never line clearly could not be used.

To access it, the Kubernetes cluster needs the ability to authenticate requests to a private registry. That way its requests will be allowed and the image successfully pulled when a new container is created.

Authenticate a request for a private image

If you are using a cloud provider for both your Kubernetes cluster and image registry, that may have its own recommended method to access it (such as IAM). However in this case, my Kubernetes cluster is running locally but I want to pull an image I’ve pushed to my private remote repository in Docker Hub.

I can push and pull images to that private repository when I run the CLI. I can do that because I’ve previously run docker login (or nerdctl login). Providing the correct password there generates a file on my local machine (cat ~/.docker/config.json in the case of a Mac). If you open that file you will see something like this:

{
  "auths": {
    "https://index.docker.io/v1/": {}
  },
  "credsStore": "osxkeychain"
}

In your case, that file may contain a token. In which case you could use that token directly from that JSON file: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials.

As you can see above, mine does not contain a token. The Mac is storing the credentials in the KeyChain. That’s not much help for the Kubernetes cluster. It needs its own JSON with a token so that it too can access the repository.

You could generate a new secret on the command line by providing it your Docker credentials. This example creates a secret called regcred in the current namespace:

kubectl create secret docker-registry regcred --docker-server=<your-registry-server> --docker-username=<your-name> --docker-password=<your-pword> --docker-email=<your-email>

For more on that approach, see: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#create-a-secret-by-providing-credentials-on-the-command-line

However that would mean typing your password on the command line. And it’s not entirely clear if the resulting token’s scope is limited to read-only 🤔. Kubernetes will likely only need to pull images from the registry and so there is no need to give it more access than it actually requires (your password gives you full access to both read and write).

You can manually create a token and so ensure that does have read-only access. Sign in to Docker Hub and visit https://hub.docker.com/settings/security:

create docker hub token

Give it a name and give it read-only access:

new docker hub token

You now need to append your Docker Hub username, a colon, and that token. Then base64-encode that, like this:

echo -n 'username:token' | base64

But Kubernetes expects a JSON file, not a string. So put that generated string in a JSON file (recall that structure from the above /.docker/config.json):

{
  "auths": {
    "https://index.docker.io/v1/": {
      "auth": "theBase64EncodedStringHere"
    }
  }
}

… and armed with that temp.json (which you can delete afterwards) you can create a Kubernetes secret from it, as if you had that /.docker/config.json to begin with:

kubectl create secret generic regcred --from-file=.dockerconfigjson=temp.json --type=kubernetes.io/dockerconfigjson --dry-run=client -o yaml

I used the --dry-run flag so it did not apply it to the cluster. It’s shown on screen. That lets us copy it to our manifest:

apiVersion: v1
kind: Secret
type: kubernetes.io/dockerconfigjson
metadata:
  name: regcred
data:
  .dockerconfigjson: anotherLongBase64ValueHere

Note: Pull secrets are stored within a namespace. Here we did not provide a namespace and so it will be stored in the default namespace.

There is one more thing to do. Your deployment(s) do not know about that new secret and so won’t use it for pulls. So where you reference a private image, make sure to add it. For example:

imagePullSecrets:
  - name: regcred # <- the name of your secret
containers:
  - name: example
    image: docker.io/username/private-image:latest

When you next apply the manifest, run kubectl get pods to make sure you don’t get any errors and that your pod is successfully created. If not, check your username and token are valid for the chosen registry. This example used Docker Hub, however those run by Amazon, Google, and so on will differ.