Learning Guides
Menu

Docker Registries

8 min readDocker for Developers

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.

PLAINTEXT
┌─────────────────────────────────────────────────────────┐
│                    Docker Registry                       │
│  ┌─────────────────────────────────────────────────┐    │
│  │  Repository: mycompany/webapp                    │    │
│  │  ├── :latest                                     │    │
│  │  ├── :1.0.0                                      │    │
│  │  ├── :1.0.1                                      │    │
│  │  └── :develop                                    │    │
│  └─────────────────────────────────────────────────┘    │
│  ┌─────────────────────────────────────────────────┐    │
│  │  Repository: mycompany/api                       │    │
│  │  ├── :latest                                     │    │
│  │  └── :2.3.0                                      │    │
│  └─────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────┘

Registry Terminology

TermDescriptionExample
RegistryServer storing imagesdocker.io, ghcr.io
RepositoryCollection of related imagesmycompany/webapp
TagSpecific version of an image1.0.0, latest
ImageRepository + tagmycompany/webapp:1.0.0

Docker Hub

Docker Hub is the default public registry with millions of images.

Creating an Account

  1. Visit hub.docker.com
  2. Sign up for a free account
  3. Verify your email address

Logging In

BASH
# 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.com

Pushing Images

BASH
# 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-tags

Warning

Make sure your image tag includes your Docker Hub username or organization. Images without a registry prefix default to Docker Hub.

Pulling Images

BASH
# 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.0

Docker 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

BASH
# 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 API

Alternative Registries

GitHub Container Registry (GHCR)

BASH
# 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:latest

Note

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)

BASH
# 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:latest

Google Artifact Registry

BASH
# 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:latest

Azure Container Registry (ACR)

BASH
# 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:latest

Private Registry

Run your own Docker registry for complete control.

Basic Registry Setup

YAML
# 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:
BASH
# 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:latest

Registry with Authentication

YAML
# 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:
BASH
# 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:5000

Registry with TLS

YAML
# 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

YAML
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

BASH
# 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 version

Semantic Version Tags

TagGets UpdatesStability
1.0.0NoneMost stable
1.0Patch versionsStable
1Minor and patchBreaking possible
latestAny versionUnstable

Git-Based Tags

BASH
# 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

BASH
# 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

BASH
# 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

BASH
# 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.0

Managing Signing Keys

BASH
# 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/trust

Cosign (Sigstore)

BASH
# 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.0

Managing Registry Images

Listing Images

BASH
# 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

BASH
# 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

BASH
# 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.yml

Mirroring and Caching

Registry as Pull-Through Cache

YAML
# 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:
BASH
# Configure Docker to use cache
# /etc/docker/daemon.json
{
  "registry-mirrors": ["http://localhost:5000"]
}

Mirroring Images

BASH
# 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.0

Automating Registry Workflows

GitHub Actions Push

YAML
# .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

YAML
# 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 }}:latest

Quick Reference

Registry URLs

RegistryURL Format
Docker Hubdocker.io/user/image or user/image
GHCRghcr.io/user/image
ECR123456789.dkr.ecr.region.amazonaws.com/image
GCRgcr.io/project/image
ACRregistry.azurecr.io/image

Common Commands

CommandPurpose
docker loginAuthenticate to registry
docker pushUpload image to registry
docker pullDownload image from registry
docker tagCreate new tag for image
docker logoutRemove credentials

In the next chapter, we'll explore container security best practices.