University of Wales Trinity Saint David
ACCA7023 - Agile and DevOps

Docker Hub and Registries

Overview

An image on your laptop is only useful to you. To run it on a server, share it with a team or deploy it from a pipeline, it has to be stored somewhere other machines can reach. That place is a registry. In this lab you will learn how images are named and versioned, judge whether a public image can be trusted, run a registry of your own, and publish the image you built in the Writing Your First Dockerfile lab to Docker Hub.

Learning Objectives

By the end of this lab you will be able to:

Concept

A registry is a server that stores images and hands them out on request. You push an image to a registry and pull it from one. Inside a registry, images are grouped into repositories, one per application, and each repository holds many versions identified by tags.

Docker Hub (hub.docker.com) is the default public registry and the one Docker uses when you do not name another. It hosts hundreds of thousands of public images, from official language runtimes to software published by individuals. Other registries you will meet include the GitHub Container Registry (ghcr.io), Azure Container Registry, Amazon Elastic Container Registry and Google Artifact Registry. Companies usually keep their own images in a private registry close to where they deploy them.

A tag such as 1.29-alpine is a label that the publisher can move to a different image at any time. latest is simply the tag used when you do not give one, and it does not necessarily mean the newest version. A digest, written sha256: followed by 64 hexadecimal characters, is calculated from the image content itself. It can never point at anything else, so pulling by digest always gives exactly the same bytes.

Images are stored as layers, and each layer is also identified by a digest. When you push or pull, only the layers the other side does not already have are transferred. Ten images built on node:22-alpine share its layers and store them once.

Tasks

Task 1 - Read an image name

Every image reference has up to four parts. Click each part of the example to see what it means.

When parts are missing Docker fills them in. The registry defaults to docker.io, the namespace for official images is library, and the tag defaults to latest. Write out the full reference that Docker actually uses for each short name.

nginx means

node:22-alpine means

jsmith/hello-api means

In ghcr.io/uwtsd/notes:2.3 the registry is and the tag is

Task 2 - Explore Docker Hub and judge trust

Anyone can publish to Docker Hub, and running an image means running its publisher's code on your machine. Search from the terminal, then open hub.docker.com in a browser and look at the nginx repository.

docker search nginx --limit 5

On the website, find each of the following and note what it tells you.

  • The Docker Official Image badge. These images are curated by Docker with the upstream project, reviewed for security and rebuilt when their base is patched.
  • The Verified Publisher badge, which appears on images from commercial companies whose identity Docker has checked.
  • The Tags tab. Look at how many tags exist, how many are variants of the same version (alpine, slim, perl), and the architectures each supports.
  • The documentation, which lists the environment variables, ports and volumes the image expects.

Now search for something like nodejs and find a repository with no badge, few pulls and no documentation. That is someone's personal upload. It might be fine, but you have no reason to trust it.

You need a Postgres database image for a university project. Which is the best choice?

The official image is maintained with the Postgres project, patched regularly and documented. Stars are easy to gather and do not imply review.

Why might a company refuse to let its servers pull directly from Docker Hub?

Organisations commonly copy approved images into their own private registry, scan them and pull only from there.

Task 3 - Tags and digests

Pull an image by tag and ask Docker for its digest.

docker pull nginx:1.29-alpine
docker image ls nginx --digests

The DIGEST column shows the content address. Copy it and pull by digest instead of by tag. Replace the digest below with yours.

docker pull nginx@sha256:PASTE_YOUR_DIGEST_HERE

Success condition: Docker reports the image is up to date and downloads nothing, because it is byte for byte the same image. In six months nginx:1.29-alpine will point at a newer, patched build. Your digest will still point at today's. Deployment tools often record the digest that was tested so that production gets exactly that, while people use tags to talk about versions.

Task 4 - Run a registry of your own

A registry is itself just an application, and it is available as an image. Run one on your machine. Port 5050 is used because 5000 is often taken, on macOS by AirPlay.

docker run -d --name registry -p 5050:5000 registry:3

To push to a registry, an image's name must start with that registry's address. docker tag adds a second name to an existing image. It does not copy anything.

docker tag hello-api:1.0 localhost:5050/hello-api:1.0
docker image ls hello-api
docker image ls localhost:5050/hello-api
docker push localhost:5050/hello-api:1.0

Both names show the same IMAGE ID. The push uploads each layer and prints the digest of the image. Registries speak a standard HTTP interface, so you can ask yours what it holds.

curl http://localhost:5050/v2/_catalog
curl http://localhost:5050/v2/hello-api/tags/list

Now add a latest tag and push that too.

docker tag hello-api:1.0 localhost:5050/hello-api:latest
docker push localhost:5050/hello-api:latest

Success condition: every layer says Layer already exists and the digest printed is the same as for 1.0. You uploaded no new data. You attached a second label to the same image. Finally, delete your local copy and pull it back from the registry.

docker image rm localhost:5050/hello-api:1.0 localhost:5050/hello-api:latest
docker pull localhost:5050/hello-api:1.0
docker rm -f registry

Task 5 - Log in to Docker Hub

Create a free account at hub.docker.com if you do not have one. Your username becomes your namespace. Then, instead of using your password from the command line, create a personal access token. In Docker Hub open Account settings, then Personal access tokens, and generate one with Read and Write access. Copy it, because it is shown only once.

A token can be limited to the access a job needs and revoked on its own without changing your password, which is why automated pipelines always use tokens.

docker login -u YOUR_DOCKERHUB_USERNAME

Paste the token when asked for a password. Success condition: Login Succeeded. Logging in also raises the number of pulls Docker Hub allows you per hour. Anonymous pulls are limited per network address, and a whole campus can share one address.

Task 6 - Publish your image

Tag the image you built in the Writing Your First Dockerfile lab with your Docker Hub namespace. It is normal to publish one build under several tags, a precise one and a moving one. In the commands below, replace YOUR_DOCKERHUB_USERNAME with your own username each time.

docker tag hello-api:1.1 YOUR_DOCKERHUB_USERNAME/hello-api:1.1
docker tag hello-api:1.1 YOUR_DOCKERHUB_USERNAME/hello-api:1
docker push YOUR_DOCKERHUB_USERNAME/hello-api:1.1
docker push YOUR_DOCKERHUB_USERNAME/hello-api:1

Success condition: your repository appears at hub.docker.com/r/YOUR_DOCKERHUB_USERNAME/hello-api with both tags. Notice that the base image layers were not uploaded. Docker Hub already has node:22-alpine, so it only received your layers. Open the repository settings and note that new repositories are public by default. Never push an image that contains a password, key or other secret, because once public it must be considered leaked.

Task 7 - Run it from anywhere

Remove every local copy of the image so that Docker has to fetch it, then run it by its public name.

docker image rm YOUR_DOCKERHUB_USERNAME/hello-api:1.1 YOUR_DOCKERHUB_USERNAME/hello-api:1
docker run -d --name from-hub -p 3000:3000 YOUR_DOCKERHUB_USERNAME/hello-api:1.1
curl http://localhost:3000
docker rm -f from-hub

Success condition: Docker prints Unable to find image locally, pulls it, and the API answers. Swap usernames with the person next to you and run each other's image. Any machine in the world with Docker can now run your application with one command, which is the real value of a registry.

Task 8 - Evaluate

A deployment script runs docker pull myteam/api:latest on every server. Describe two problems this could cause.

First, latest moves, so two servers that pull at different times can end up running different versions, and nobody can say exactly what is running. Second, a rollback is not possible by tag alone, because latest now points at the broken build and the previous image has no name the script knows. A third issue is that a broken push to latest reaches every server on its next pull. Deploying a specific version tag or, better still, the digest that was tested avoids all of these.

You are about to use an image from Docker Hub that has no badge. What would you check before running it in a project?

Who published it and whether the source and Dockerfile are available to read. When it was last updated and whether it is maintained. Whether an official or verified alternative exists. What the image actually contains, for example by reading docker history and scanning it for known vulnerabilities. And whether it is pinned to a version or digest so it cannot change under you after you have checked it.

Explain why pushing latest in Task 4 took no time and uploaded nothing.

Registries store layers by digest and an image is a manifest listing its layer digests. The latest tag pointed at the same image as 1.0, so every layer and the manifest already existed in the registry. The push only had to record a new tag name pointing at an existing manifest.