Topic 5.3
Container Registries: ECR, GHCR, and Docker Hub
In one line
A container registry is an artifact repository specifically for Docker images — the exact same build-once-store-retrieve principle from Topic 5.1, applied to the images Docker's own course taught you to build.
Key ideas
- 01
A CONTAINER REGISTRY is genuinely just a specialized artifact repository (Topic 5.1's principle applies identically) storing container IMAGES specifically, addressable by a name and TAG (
my-registry/my-app:1.5.0) —docker pushanddocker pull(Docker's own course) are the exact commands used to publish to and retrieve from one. - 02
DOCKER HUB is the original, most widely-known public registry — genuinely fine for public, open-source images, but real organizations building PRIVATE, proprietary images almost always use a different registry with better organizational access control and, often, tighter integration with their existing cloud or CI platform.
- 03
AWS ECR (Elastic Container Registry) integrates directly with AWS's own IAM permission system (the DevOps roadmap this course draws from covers IAM in its own dedicated AWS course) — genuinely the natural choice for a team already deploying to AWS (ECS, EKS — Kubernetes' own course covered EKS as a managed Kubernetes offering), since authentication and access control flow directly from infrastructure the team already manages.
- 04
GHCR (GitHub Container Registry) integrates directly with GitHub itself — genuinely convenient for a team already using GitHub Actions (Phase 2), since authentication can flow automatically from the same
GITHUB_TOKENa workflow already has access to, with zero separate registry credentials to manage at all. - 05
IMAGE TAGGING STRATEGY matters genuinely as much as the registry choice itself — tagging every build with a genuinely UNIQUE identifier (a commit SHA, or a real semantic version, Git's own course Phase 6.3) rather than always overwriting a generic
latesttag is what actually makes 'roll back to the previous version' (Kubernetes' own course, Phase 6.3) a meaningful, reliable operation —latestby definition can't distinguish one specific previous version from another. - 06
A genuinely common, correct real pattern: build once, tag the resulting image with BOTH a specific, unique identifier (
myapp:a1b2c3dormyapp:1.5.0) AND, separately, update a floating tag likemyapp:latestto also point at that same image — giving both a precise, permanent reference for deployments/rollbacks and a convenient, human-friendly 'whatever's newest' reference for casual use.
Code & diagrams
Tagging with both a unique identifier and a floating 'latest' — the standard, correct pattern.
# Build once
docker build -t myapp:build .
# Tag with a genuinely unique, permanent identifier — never reused
docker tag myapp:build my-registry/myapp:$CI_COMMIT_SHA
# ALSO tag as latest, for convenience — this one DOES get overwritten each time
docker tag myapp:build my-registry/myapp:latest
# Push BOTH tags
docker push my-registry/myapp:$CI_COMMIT_SHA
docker push my-registry/myapp:latest
# A rollback references the SPECIFIC, permanent tag — never "latest",
# since "latest" by definition can't tell you which specific previous version it was
# kubectl set image deployment/myapp myapp=my-registry/myapp:a1b2c3d (Kubernetes course, Phase 6.3)GHCR's own convenience — zero separate credentials needed inside a GitHub Actions workflow.
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
packages: write # grants this job's own GITHUB_TOKEN registry access
steps:
- uses: actions/checkout@v4
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} # already exists — no separate secret needed
- name: Build and push
run: |
docker build -t ghcr.io/my-org/myapp:${{ github.sha }} .
docker push ghcr.io/my-org/myapp:${{ github.sha }}Explain it without notes
Why is tagging every image build with a unique identifier like a commit SHA, in addition to latest, genuinely important for being able to roll back reliably?
Why might a team already deploying to AWS specifically choose ECR over Docker Hub for their private images?
Practice
Push an image to a registry you have access to (Docker Hub, GHCR, or ECR) with both a unique tag and a latest tag, and confirm both tags are visible in the registry pointing at the same image.
If you use GitHub Actions, set up image push authentication using GITHUB_TOKEN against GHCR, confirming you never needed to create or store a separate registry credential.
Trade-offs
- ↔
A registry tightly integrated with your existing platform (ECR with AWS, GHCR with GitHub) reduces credential management overhead significantly, but it does create a real degree of platform coupling — moving to a different cloud provider or Git host later means also migrating your container registry and updating every pipeline and deployment reference to it, a genuine, real cost worth being aware of even if it's rarely the deciding factor for most teams' initial choice.
Done when you can
I understand a container registry is just an artifact repository specialized for container images.
I can explain why tagging with a unique identifier (not just latest) matters for reliable rollbacks.
I can reason about when ECR, GHCR, or Docker Hub is the most natural choice for a given team's existing platform.