We Need to Talk About Docker Registries
Tags: docker, cicd
Published: 2022-11-09 07:22:11

*By: [Peter Solymos](https://hosting.analythium.io/author/peter/)*

**A Docker registry stores Docker images, this is where we push images to and pull images from. Why do we need a registry? What registries are out there? How can we work with them? Read on to find out.**

Many Docker tutorials begin with `docker pull` as the first command to cover. The `docker pull` command is followed by the "tag" of a Docker image that we want to be pulled. Say that we wanted the latest Alpine Linux image (I picked this due to its minimal size):

``` bash
docker pull alpine:latest

# latest: Pulling from library/alpine
# 530afca65e2e: Pull complete
# Digest: sha256:7580ece7963bfa...
# Status: Downloaded newer image for alpine:latest
# docker.io/library/alpine:latest
```

But where does this image come from?

## What is a Docker registry?

Where does the Docker command line interface (CLI for short) pulls the images from? The answer is on the last line of the output. `docker.io` is the URL for a so called **Docker registry**. In this case, it refers to the default registry, [Docker Hub](https://hub.docker.com/), applied when you don't specify a registry URL as part of the pull command.

Docker Hub is a service provided by Docker for finding and sharing container images.

The registry URL is usually followed by the user or organization ID, the [repository](https://docs.docker.com/docker-hub/repos/) name, the image name and the tag: `<registry>/<user>/<repository>/<image>:<tag>`.

In short, a [Docker registry](https://docs.docker.com/registry/) stores Docker **images**. This is where we push images to and pull images from:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/04/architecture.png" class="kg-image" loading="lazy" srcset="https://hosting.analythium.io/content/images/size/w600/2021/04/architecture.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/04/architecture.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/04/architecture.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/04/architecture.png 2400w" sizes="(min-width: 720px) 720px" width="2000" height="1124" alt="Docker architecture / © Analythium" />
<figcaption aria-hidden="true">Docker architecture / © Analythium</figcaption>
</figure>

Read more about how the different parts of the Docker system relate to each other in this introductory post:

<figure>
<img src="https://hosting.analythium.io/assets/icon-192x192.png?v=53de193d60" class="kg-bookmark-icon" />
</figure>

## Why do you need a registry?

You might think that you only need a registry because you need to pull your parent images (like Ubuntu, Alpine, `r-base` from the [Rocker project](https://rocker-project.org/) etc.) from somewhere. Then you can build your Docker images locally using a Dockerfile, and run them. Why bother with a registry at all?

You can even use the `docker save` and `docker load` commands to save to a compressed tar file:

``` bash
docker pull alpine:latest

docker save -o alpine-latest.tar alpine:latest

ls -sh alpine-latest.tar
# 11368 alpine-latest.tar
```

Then take this tar file, copy it to another server and load it:

``` bash
docker load --input alpine-latest.tar
```

now imagine that you are managing more than two servers, or you want to share the Docker image with others (to use it or to serve as a parent image). The save/copy/load workflow becomes cumbersome.

In this case, using a registry might be a much better idea. Luckily, there are many options to choose from, and you can even host your own registry.

## Roll your own registry

If you want a [registry hosted on your machine](https://docs.docker.com/registry/deploying/), just pull the registry image. The next command will pull the `registry` image, and run the similarly named container in the background on port 5000:

``` bash
docker run -d \
  -p 5000:5000 \
  --restart=always \
  --name registry registry:2
```

Tag the Alpine image with the URL of your local registry, `localhost:5000`, and push the image:

``` bash
docker tag alpine:latest localhost:5000/my-alpine

docker push localhost:5000/my-alpine
```

Remove the images so the `docker images` will not list these anymore:

``` bash
docker image remove alpine:latest
docker image remove localhost:5000/my-alpine
```

Pull the image from the local registry and list it with `docker images` to verify that it is there again:

``` bash
docker pull localhost:5000/my-alpine
```

Stop and remove the registry container. The `-v` option makes sure to remove anonymous volumes associated with the container which is often used to mount a volume from your hard drive into the container where the images are stored:

``` bash
docker container stop registry && \
  docker container rm -v registry
```

If you want your own registry to be accessed over a network, then you need to think about security and access control.

Setting up [transport layer security](https://blog.alexellis.io/get-a-tls-enabled-docker-registry-in-5-minutes/) (TLS) for HTTPS and [user authentication](https://docs.docker.com/registry/deploying/#restricting-access) are all advanced topics, so implement those at your own risk.

For now, we will continue with a hosted registry, as most people do.

## Log into a registry

When you work with private registries or private images, you need to log in with the `docker login` command. For Docker Hub, just type `docker login`. For  all other registries, type in the registry URL as well, e.g. `docker login ghcr.io`.

The Docker CLI then will prompt you for your username and password (or access token).

You can log in programmatically by providing your username and the password through standard input:

``` shell
export CR_PAT=YOUR_TOKEN
echo $CR_PAT | docker login ghcr.io -u USER --password-stdin
```

As an alternative, the password can be provided via a file, this approach won't leave trace of your token in your history:

``` shell
cat ~/my_password.txt | docker login -u USER --password-stdin
```

Use one of these approaches to log into any public or private repository for which you have credentials. The credentials will be [stored locally](https://docs.docker.com/engine/reference/commandline/login/#privileged-user-requirement): in `$HOME/.docker/config.json` on Linux or `%USERPROFILE%/.docker/config.json` on Windows. After login, there is no need to re-authenticate until you log out with `docker logout`.

Note that `docker login` requires users to use `sudo` or be `root` in most cases.

It is always a good idea to use a token instead of your password. Tokens can have limited scope (i.e. only for pulling images), and can be revoked at any time without it impacting other areas of your life.

## Commonly used registries

There are many registries out there besides Docker Hub. Here is a non-exhaustive list of options.

[GitHub container registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry) (the `ghcr.io` URL from above) is available as part of GitHub Packages for free and paid plans, even for private repositories under the free plan. This registry requires authentication using your GitHub token.

An alternative to GitHub is [GitLab](https://about.gitlab.com/) (`registry.gitlab.com`), which has provided [registry support](https://about.gitlab.com/blog/2016/05/23/gitlab-container-registry/) for its free (public and private) repositories long before GitHub. The docs are also much better in my opinion, and the whole experience is tightly integrated with GitLab's CICD pipelines. This registry also needs login with a token.

[Heroku comes with a Docker registry](https://devcenter.heroku.com/articles/container-registry-and-runtime) (`registry.heroku.com`) where the Docker-based deploys push the images to.

Of course, every major cloud provider offers a Docker container registry that is often integrated with their other offerings. Latency should be minimal due to network proximity to the servers:

- [Amazon Elastic Container Registry (Amazon ECR)](https://aws.amazon.com/ecr/)
- [Azure Container Registry](https://azure.microsoft.com/en-us/services/container-registry/)
- [Google Container Registry](https://cloud.google.com/container-registry)
- [DigitalOcean Container Registry](https://www.digitalocean.com/products/container-registry)

Other common alternatives for container registries, Helm charts, etc., include:

- [JFrog Container Registry](https://jfrog.com/container-registry/)
- [Harbor](https://goharbor.io/)
- [Scaleway](https://www.scaleway.com/en/container-registry/)

Although these services are called "container registry", but strictly speaking they store container images.

## Summary

We reviewed what container registries are, how to access them, and what are the commonly used services out there that you can leverage for your workflow.

You have now a good understanding of where the images go after push and where the images come from after pull.

## Further readings

- [The official Docker docs](https://docs.docker.com/registry/)
- [Docker Hub and Docker registries: A beginner’s guide](https://jfrog.com/knowledge-base/docker-hub-and-docker-registries-a-beginners-guide/)
- [CICD patterns with GitHub Actions and Docker](https://hosting.analythium.io/cicd-patterns-with-github-actions-and-docker/)
- [How to set up a private docker registry on Ubuntu 20.04](https://www.digitalocean.com/community/tutorials/how-to-set-up-a-private-docker-registry-on-ubuntu-20-04)
- [Get a TLS-enabled Docker registry in 5 minutes](https://blog.alexellis.io/get-a-tls-enabled-docker-registry-in-5-minutes/)