Docker Registries
Docker Registries
A Docker registry is a service that stores and distributes Docker images. This chapter covers Docker Hub, private registries, and strategies for managing and distributing your images.
Understanding Registries
Registries are to Docker images what GitHub is to code—a centralized place to store, version, and distribute your work.
┌─────────────────────────────────────────────────────────┐
│ Docker Registry │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Repository: mycompany/webapp │ │
│ │ ├── :latest │ │
│ │ ├── :1.0.0 │ │
│ │ ├── :1.0.1 │ │
│ │ └── :develop │ │
│ └─────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Repository: mycompany/api │ │
│ │ ├── :latest │ │
│ │ └── :2.3.0 │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘Registry Terminology
| Term | Description | Example |
|---|---|---|
| Registry | Server storing images | docker.io, ghcr.io |
| Repository | Collection of related images | mycompany/webapp |
| Tag | Specific version of an image | 1.0.0, latest |
| Image | Repository + tag | mycompany/webapp:1.0.0 |
Docker Hub
Docker Hub is the default public registry with millions of images.
Creating an Account
- Visit hub.docker.com
- Sign up for a free account
- Verify your email address
Logging In
# Login to Docker Hub
docker login
# Login with username
docker login -u myusername
# Login to a different registry
docker login ghcr.io
docker login registry.example.comPushing Images
# Tag image for Docker Hub
docker tag myapp:latest myusername/myapp:latest
docker tag myapp:latest myusername/myapp:1.0.0
# Push to Docker Hub
docker push myusername/myapp:latest
docker push myusername/myapp:1.0.0
# Push all tags
docker push myusername/myapp --all-tagsWarning
Make sure your image tag includes your Docker Hub username or organization. Images without a registry prefix default to Docker Hub.
Pulling Images
# Pull from Docker Hub (implicit)
docker pull nginx
# Explicit Docker Hub reference
docker pull docker.io/library/nginx:latest
# Pull user image
docker pull myusername/myapp:1.0.0Docker Hub Features
Public Repositories: Free, unlimited public repos Private Repositories: Limited on free tier Automated Builds: Build from GitHub/Bitbucket Webhooks: Trigger actions on push Organizations: Team collaboration
Managing Repositories
# List images in your repository (via Docker Hub UI or API)
# Use Docker Hub website for repository management
# Delete local images
docker rmi myusername/myapp:old-tag
# Delete from registry requires Hub UI or APIAlternative Registries
GitHub Container Registry (GHCR)
# Login with GitHub token
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
# Tag for GHCR
docker tag myapp:latest ghcr.io/myusername/myapp:latest
# Push
docker push ghcr.io/myusername/myapp:latest
# Pull
docker pull ghcr.io/myusername/myapp:latestNote
GHCR is free for public images and integrates seamlessly with GitHub Actions.
Use a Personal Access Token (PAT) with read:packages and write:packages
scopes.
Amazon Elastic Container Registry (ECR)
# Login to ECR (using AWS CLI)
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin \
123456789012.dkr.ecr.us-east-1.amazonaws.com
# Create repository
aws ecr create-repository --repository-name myapp
# Tag and push
docker tag myapp:latest 123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp:latest
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp:latestGoogle Artifact Registry
# Configure Docker authentication
gcloud auth configure-docker us-docker.pkg.dev
# Tag and push
docker tag myapp:latest us-docker.pkg.dev/myproject/myrepo/myapp:latest
docker push us-docker.pkg.dev/myproject/myrepo/myapp:latestAzure Container Registry (ACR)
# Login to ACR
az acr login --name myregistry
# Tag and push
docker tag myapp:latest myregistry.azurecr.io/myapp:latest
docker push myregistry.azurecr.io/myapp:latestPrivate Registry
Run your own Docker registry for complete control.
Basic Registry Setup
# docker-compose.yml
services:
registry:
image: registry:2
ports:
- "5000:5000"
volumes:
- registry-data:/var/lib/registry
environment:
REGISTRY_STORAGE_DELETE_ENABLED: "true"
volumes:
registry-data:# Start registry
docker compose up -d
# Tag and push to local registry
docker tag myapp:latest localhost:5000/myapp:latest
docker push localhost:5000/myapp:latest
# Pull from local registry
docker pull localhost:5000/myapp:latestRegistry with Authentication
# docker-compose.yml
services:
registry:
image: registry:2
ports:
- "5000:5000"
volumes:
- registry-data:/var/lib/registry
- ./auth:/auth
environment:
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
volumes:
registry-data:# Create password file
mkdir auth
docker run --entrypoint htpasswd registry:2 -Bbn admin password > auth/htpasswd
# Start registry
docker compose up -d
# Login
docker login localhost:5000Registry with TLS
# docker-compose.yml
services:
registry:
image: registry:2
ports:
- "443:443"
volumes:
- registry-data:/var/lib/registry
- ./certs:/certs
- ./auth:/auth
environment:
REGISTRY_HTTP_ADDR: 0.0.0.0:443
REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt
REGISTRY_HTTP_TLS_KEY: /certs/domain.key
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
volumes:
registry-data:Warning
Always use TLS in production registries. Without it, Docker requires adding the registry to insecure-registries in the Docker daemon config.
Registry UI
services:
registry:
image: registry:2
volumes:
- registry-data:/var/lib/registry
registry-ui:
image: joxit/docker-registry-ui
ports:
- "8080:80"
environment:
- REGISTRY_TITLE=My Registry
- REGISTRY_URL=http://registry:5000
- DELETE_IMAGES=true
depends_on:
- registry
volumes:
registry-data:Image Tagging Strategies
Semantic Versioning
# Tag with semantic versions
docker tag myapp:latest myrepo/myapp:1.0.0
docker tag myapp:latest myrepo/myapp:1.0
docker tag myapp:latest myrepo/myapp:1
docker tag myapp:latest myrepo/myapp:latest
# Users can pin to different levels
docker pull myrepo/myapp:1.0.0 # Exact version
docker pull myrepo/myapp:1.0 # Minor updates
docker pull myrepo/myapp:1 # Major versionSemantic Version Tags
| Tag | Gets Updates | Stability |
|---|---|---|
1.0.0 | None | Most stable |
1.0 | Patch versions | Stable |
1 | Minor and patch | Breaking possible |
latest | Any version | Unstable |
Git-Based Tags
# Tag with git commit
GIT_SHA=$(git rev-parse --short HEAD)
docker tag myapp:latest myrepo/myapp:${GIT_SHA}
# Tag with branch name
BRANCH=$(git rev-parse --abbrev-ref HEAD | tr '/' '-')
docker tag myapp:latest myrepo/myapp:${BRANCH}
# Combine version and commit
docker tag myapp:latest myrepo/myapp:1.0.0-${GIT_SHA}Environment-Based Tags
# Environment-specific tags
docker tag myapp:latest myrepo/myapp:staging
docker tag myapp:latest myrepo/myapp:production
# Date-based tags
DATE=$(date +%Y%m%d)
docker tag myapp:latest myrepo/myapp:${DATE}Best Practices
# Always use specific tags in production
FROM myrepo/myapp:1.0.0
# Never use :latest in production
# FROM myrepo/myapp:latest # Avoid!
# Use digest for absolute immutability
FROM myrepo/myapp@sha256:abc123...Note
Tags can be overwritten—myapp:1.0 today might be different from myapp:1.0
tomorrow. Use image digests for guaranteed reproducibility.
Image Signing and Trust
Docker Content Trust
# Enable content trust
export DOCKER_CONTENT_TRUST=1
# Push signed image
docker push myrepo/myapp:1.0.0
# First push creates signing keys
# Pull only signed images
docker pull myrepo/myapp:1.0.0Managing Signing Keys
# List signing keys
notary key list
# Rotate signing keys
notary key rotate myrepo/myapp snapshot
# Backup keys (important!)
tar cvzf docker-trust-keys.tar.gz ~/.docker/trustCosign (Sigstore)
# Install cosign
brew install cosign
# Generate key pair
cosign generate-key-pair
# Sign image
cosign sign --key cosign.key myrepo/myapp:1.0.0
# Verify signature
cosign verify --key cosign.pub myrepo/myapp:1.0.0Managing Registry Images
Listing Images
# List repositories (registry API)
curl -X GET http://localhost:5000/v2/_catalog
# List tags for a repository
curl -X GET http://localhost:5000/v2/myapp/tags/list
# For Docker Hub, use the Hub API
curl -s "https://hub.docker.com/v2/repositories/library/nginx/tags?page_size=100" | jq '.results[].name'Cleaning Up Images
# Delete local unused images
docker image prune
# Delete all unused images
docker image prune -a
# Delete specific image from registry (if enabled)
curl -X DELETE http://localhost:5000/v2/myapp/manifests/<digest>Registry Garbage Collection
# Run garbage collection on registry
docker exec registry bin/registry garbage-collect /etc/docker/registry/config.yml
# Dry run
docker exec registry bin/registry garbage-collect --dry-run /etc/docker/registry/config.ymlMirroring and Caching
Registry as Pull-Through Cache
# docker-compose.yml
services:
registry-cache:
image: registry:2
ports:
- "5000:5000"
volumes:
- cache-data:/var/lib/registry
environment:
REGISTRY_PROXY_REMOTEURL: https://registry-1.docker.io
volumes:
cache-data:# Configure Docker to use cache
# /etc/docker/daemon.json
{
"registry-mirrors": ["http://localhost:5000"]
}Mirroring Images
# Copy image between registries
docker pull source-registry/myapp:1.0
docker tag source-registry/myapp:1.0 dest-registry/myapp:1.0
docker push dest-registry/myapp:1.0
# Using skopeo (more efficient)
skopeo copy \
docker://source-registry/myapp:1.0 \
docker://dest-registry/myapp:1.0Automating Registry Workflows
GitHub Actions Push
# .github/workflows/docker.yml
name: Build and Push
on:
push:
branches: [main]
tags: ["v*"]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
myuser/myapp:latest
myuser/myapp:${{ github.sha }}Multi-Registry Push
# Push to multiple registries
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
myuser/myapp:latest
ghcr.io/${{ github.repository }}:latestQuick Reference
Registry URLs
| Registry | URL Format |
|---|---|
| Docker Hub | docker.io/user/image or user/image |
| GHCR | ghcr.io/user/image |
| ECR | 123456789.dkr.ecr.region.amazonaws.com/image |
| GCR | gcr.io/project/image |
| ACR | registry.azurecr.io/image |
Common Commands
| Command | Purpose |
|---|---|
docker login | Authenticate to registry |
docker push | Upload image to registry |
docker pull | Download image from registry |
docker tag | Create new tag for image |
docker logout | Remove credentials |
In the next chapter, we'll explore container security best practices.