11 - Image Registries¶
What this session is¶
About 30 minutes. How to share images: log in to a registry, tag for upload, push, pull. The two big ones - Docker Hub and GitHub Container Registry (GHCR).
What a registry is¶
A registry is just a server that stores images. docker pull alpine contacts Docker Hub. docker pull ghcr.io/owner/image contacts GitHub Container Registry. Any compliant server can host a registry; you can even run one yourself.
The biggest public registries:
| Registry | URL prefix | Notes |
|---|---|---|
| Docker Hub | docker.io/ (often omitted) |
The default; largest ecosystem |
| GitHub Container Registry | ghcr.io/ |
Free for OSS; tightly integrated with GitHub |
| Google Artifact Registry | <region>-docker.pkg.dev/ |
Cloud-native |
| AWS ECR | <account>.dkr.ecr.<region>.amazonaws.com/ |
AWS-specific |
| Azure Container Registry | <name>.azurecr.io/ |
Azure-specific |
For your first contributions, Docker Hub and GHCR are the relevant ones.
Image references, full form¶
REGISTRY- defaults todocker.iowhen omitted.NAMESPACE- your username, org, orlibrary(for official images).NAME- the image name.TAG- version label.DIGEST- content hash (immutable).
Examples (same image, four ways):
When you docker pull, the short forms work; the daemon fills in defaults.
Log in¶
docker login # Docker Hub
docker login ghcr.io # GitHub Container Registry
docker login <other-registry> # other
For Docker Hub, use your dockerhub username + password (or a personal access token - recommended).
For GHCR, use your GitHub username + a Personal Access Token (PAT) with write:packages scope. Generate one at github.com/settings/tokens.
Your credentials are stored in ~/.docker/config.json. For security, modern Docker uses your OS keychain on macOS/Windows by default.
Tag an image for upload¶
To push to a registry, the image must be tagged with the registry's prefix:
docker tag myimage:1.0 myname/myimage:1.0 # Docker Hub
docker tag myimage:1.0 ghcr.io/myname/myimage:1.0 # GHCR
docker tag SOURCE TARGET doesn't copy - it adds a new label to the same image. After tagging, both names refer to the same image. Remove either via docker rmi; the underlying image stays as long as one tag points to it.
Push¶
Watches each layer upload (only changed layers transfer - Docker compares hashes).
After a push, visit Docker Hub or GHCR in your browser. You should see the image. Add a README on Docker Hub's UI; verify visibility (public vs private - check the settings).
Pull on another machine¶
If private, you need to docker login first.
Multi-arch images¶
Modern registries support multi-arch manifests: one tag points to several architecture-specific images. nginx:1.27 resolves to the right one for your host (amd64, arm64, etc.).
Build multi-arch yourself with docker buildx:
docker buildx create --name multiarch --use
docker buildx build --platform linux/amd64,linux/arm64 -t myname/myimage:1.0 --push .
That builds both architectures in one go and pushes a single multi-arch manifest. Useful when you publish for both x86 servers and ARM (Macs, Raspberry Pis).
Pull-through caches¶
Big setups run a local registry as a pull-through cache - pulls from your local one, which only contacts Docker Hub on cache misses. Lessens hit on Docker Hub rate limits (free Docker Hub limits per-IP pull rate); faster pulls in your network.
registry:2 is the official open-source image. Run it; configure your Docker daemon (daemon.json) to use it. Beyond beginner; recognize the pattern.
Self-hosted registries¶
docker run -d -p 5000:5000 registry:2 runs a private registry on localhost:5000. Push/pull:
docker tag myimage:1.0 localhost:5000/myimage:1.0
docker push localhost:5000/myimage:1.0
docker pull localhost:5000/myimage:1.0
For shared internal use, you'd also want TLS, auth, and storage backed by something durable (S3, GCS). The defaults are insecure-by-design for local-only testing.
Public vs private¶
Both Docker Hub and GHCR support both. By default:
- Docker Hub: new repos are public unless you have a paid plan.
- GHCR: inherits the parent repo's visibility (public if your repo is public, private if not). For org-owned images, configure in package settings.
Public images can be pulled by anyone, no auth. Private require docker login.
CI: building and pushing on every commit¶
A typical GitHub Actions workflow:
name: Build and push image
on: { push: { branches: [main] } }
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}:latest
Every push to main rebuilds and pushes to ghcr.io/owner/repo:latest. You'll see this exact pattern in many OSS Rust/Go/Python projects.
Exercise¶
You need a Docker Hub account (free at hub.docker.com) and/or a GitHub PAT for GHCR.
-
Tag and push to Docker Hub:
Open Docker Hub in a browser. Find your image. -
Pull from another tag:
-
Push to GHCR:
Visit GitHub → your profile → Packages. Find the image. Optionally make it public from the package settings. -
Pull-through cache (advanced):
What you might wonder¶
"What's the difference between docker push and the image actually appearing?"
After push, Docker Hub processes the upload; usually visible immediately. GHCR shows it under your packages quickly too.
"How do I delete images from a registry?"
Docker Hub: via the web UI. GHCR: via GitHub Packages UI. Programmatically via each registry's API. There's no docker rm for remote images; pushing again with the same tag overwrites.
"What about image signing?"
For real supply-chain integrity, sign images with cosign (sigstore). Verify at deploy. The "Container Internals" senior reference path covers this. Beyond beginner.
"Docker Hub rate limits?" Free anonymous pulls: ~100/6 hours per IP. Logged-in free: 200/6h. Paid plans: higher or unlimited. For CI on free tier, log in or use a registry mirror.
Done¶
- Tag images for a registry.
- Push to Docker Hub and GHCR.
- Pull (private images need
docker loginfirst). - Recognize multi-arch images.
- Know about pull-through caches and self-hosted registries.
Next: Reading other people's Dockerfiles →