Course outline · 0% complete

0/29 lessons0%

Course overview →

Tags, push, and pull

lesson 6-1 · ~12 min · 17/29

From lesson 3-1, the -t in docker build -t myapp:v1 . tagged the resulting image with a name and version.

It names the image myapp and tags it v1, and so far that image exists only on the machine that built it.

This lesson is about moving it to a registry, so other machines, including the CI pipeline in unit 7, can pull it.

Registries

So far the image exists only on the machine that built it, which means nothing else can run it: not the server being deployed to, not a teammate, not the test automation of unit 7.

Moving images between machines is what a registry is for, a server that stores and serves images, the equivalent of git hosting but for images instead of code. You have already used one, since docker run hello-world in lesson 2-1 pulled from Docker Hub, the default public registry. The other one that comes up constantly is GitHub Container Registry, GHCR, which lives next to your repos.

A full image reference has up to four parts:

ghcr.io/ada/shop:v2
└─────┘ └─┘ └──┘ └┘
registry owner name tag
PartExampleDefault when omitted
registryghcr.iodocker.io
owneradalibrary
nameshopnone, always required
tagv2latest

Writing just nginx makes Docker fill in those defaults, giving docker.io/library/nginx:latest.

Shipping an image is three commands:

docker login ghcr.io                      # authenticate once
docker tag myapp:v1 ghcr.io/ada/shop:v1   # give the image its full remote name
docker push ghcr.io/ada/shop:v1           # upload it

Any other machine then runs docker pull ghcr.io/ada/shop:v1. Thanks to layers from lesson 3-2, pushes and pulls only transfer layers the other side does not already have, which is why the second push of a small change is fast.

Building references from parts

References are plain strings, so a shell can assemble them from a repo and a tag.

repo="ghcr.io/ada/shop"
tag="v2"
echo "$repo:$tag"
echo "$repo:latest"

Output

ghcr.io/ada/shop:v2
ghcr.io/ada/shop:latest

The reference format is repo:tag, joined by a colon, and echo "$repo:$tag" expands both variables around it. A colon is not a valid character in a bash variable name, which is why no braces are needed here.

LineProduces
echo "$repo:$tag"the versioned reference
echo "$repo:latest"the moving reference

Release scripts do exactly this, pushing both the immutable version tag and the moving latest tag from the same build.

How ordinary the latest tag really is

Nothing is special about latest. It is simply the default tag name, and it only moves when someone explicitly pushes to it.

latest is an ordinary tag that happens to be assumed when no tag is given. Nobody updates it automatically, and teams can and do push older builds to it by accident.

Belief about latestReality
always the newest buildonly if someone pushed it there
updated automaticallyno
immutableno, it moves

That is why lesson 2-1 insisted on pinning real version tags, and why rollbacks in lesson 8-3 rely on immutable tags such as v41. A deploy that references latest cannot be reproduced later, because the same string will point at different bytes.

Expanding a bare image name

A plain nginx in a docker run command actually resolves to docker.io/library/nginx:latest.

Bare names get three defaults filled in: the registry docker.io, which is Docker Hub, the owner library, which is the namespace for official images, and the tag latest.

WrittenResolved
nginxdocker.io/library/nginx:latest
ada/shopdocker.io/ada/shop:latest
ghcr.io/ada/shop:v1unchanged, fully specified

Knowing the expansion matters the day another registry is used, where every part must be written out. It also explains why an official image and a personal one with the same name never collide, since the owner differs.

The subcommand that uploads an image

The subcommand is docker push, the same verb git uses to upload commits, and its opposite is docker pull.

After docker login and docker tag give the image its full remote name, docker push uploads any layers the registry does not already have.

StepCommand
authenticatedocker login ghcr.io
name it for the remotedocker tag myapp:v1 ghcr.io/ada/shop:v1
uploaddocker push ghcr.io/ada/shop:v1

In unit 7 the CI pipeline runs this command on every merge, so pushing by hand becomes something you do mainly while learning or debugging.