What a private Docker registry is

A registry is the server that stores container images and serves them when someone runs docker pull. Docker Hub is the default public registry. As soon as you need to control where your images live, for compliance, for bandwidth, or to stop depending on a third party, you run your own. The reference implementation comes from the Distribution project, packaged as the registry:3 image.

How tagging and pushing an image works

An image name has the registry encoded up front: host:port/repository:tag. docker tag alpine:3.21 registry.lab:5000/alpine:mine copies nothing; it just creates a local alias pointing at the same image ID, and it is docker push that actually uploads the layers to the registry. The source behind that push endpoint lives in the distribution/distribution repository.

Securing the registry with htpasswd and TLS

An unauthenticated registry is an open bin: anyone with network access to port 5000 can push or pull images. In this lab we lock it down for real. We generate our own CA and a certificate for registry.lab, create the htpasswd file with a bcrypt hash and start registry:3 with REGISTRY_HTTP_TLS_CERTIFICATE, REGISTRY_AUTH=htpasswd and REGISTRY_AUTH_HTPASSWD_PATH.

The Docker client trusts the certificate because we copy the CA to /etc/docker/certs.d/registry.lab:5000/ca.crt, as Docker's registry certificate documentation describes. The result shows in the terminal: without credentials, the API answers 401 and the push fails. After docker login, push and pull work over HTTPS. The user and password (changeme) are throwaway values: in production use a strong password and a certificate from a recognised CA.

If you’re exposing the registry under your own domain, our Nginx Proxy Manager on Docker guide covers the TLS certificate side for any container. To understand the rest of the networking this lab touches, we have dedicated guides on Docker networking and volumes and bind mounts. If you’d rather read a written walkthrough without the video, the long-form version is how to run a private Docker image registry. This and the rest of our hands-on labs follow the same rule: everything you see actually ran.

FAQ

What is the official image used to run a private Docker registry?

The registry:3 image, maintained by the Distribution project (the reference implementation of the container registry). It replaces the older registry:2, which still works but no longer gets the latest fixes.

How do I tell Docker to use my registry instead of Docker Hub?

By prefixing the image name with the registry's host and port: registry.lab:5000/alpine:mine instead of alpine:mine. That prefix is the only thing that tells Docker Hub images apart from images in your own registry.

Is it safe to leave a Docker registry with no authentication or TLS?

Not for production. Without authentication, anyone with network access to port 5000 can push or pull images; without TLS, credentials travel in clear text. In this lab we turn both on: the registry serves HTTPS with a certificate from our own CA and requires a user and password, so without credentials the API answers 401 and both push and pull fail.

What is the difference between docker tag and docker push?

docker tag doesn't move a single byte: it creates a new local alias pointing at the same image ID. docker push is the command that actually ships the image's layers to the registry encoded in that alias.

How do I make Docker trust my private registry's certificate?

Copy the certificate of the CA that signed it to /etc/docker/certs.d/<host>:<port>/ca.crt on every client machine, for example /etc/docker/certs.d/registry.lab:5000/ca.crt. Docker reads that directory on every connection, with no daemon restart, and the trust is limited to that registry.