Skip to content

Docker Registry

  • A Docker Registry is a service that stores and distributes Docker images. It’s the central repository from which images are pulled and to which they are pushed.
  • Images are identified as [registry-host/][namespace/]name[:tag]
  • Default registry: Docker Hub (docker.io). If no registry is specified in an image name, Docker Hub is assumed.
RegistryProviderTypeNotes
Docker HubDocker IncPublic/PrivateDefault. Free tier has pull rate limits.
GitHub Container Registry (GHCR)GitHubPublic/PrivateIntegrated with GitHub Actions.
Azure Container Registry (ACR)MicrosoftPrivateNative AKS/Azure integration. Geo-replication.
AWS ECRAmazonPrivateNative ECS/EKS integration. IAM auth.
Google Artifact RegistryGooglePrivateReplaced GCR. Multi-format (Docker, npm, Maven).
HarborCNCFSelf-hostedOpen-source. Vulnerability scanning, RBAC.
Nexus RepositorySonatypeSelf-hostedMulti-format proxy and private registry.
Terminal window
# Login to Docker Hub
docker login
# ⚠️ Credentials are stored as base64 in ~/.docker/config.json — not encrypted.
# Use a credential helper (docker-credential-pass, docker-credential-osxkeychain)
# to store them securely. See: docker help login
# Login to a private registry
docker login myregistry.azurecr.io
# Pull an image from Docker Hub (implicit registry)
docker pull nginx:alpine
# Pull from a private registry (must be logged in)
docker pull myregistry.azurecr.io/apps/my-app:v1.2
# Tag an existing local image for a registry
docker tag my-app:latest myregistry.azurecr.io/team/my-app:v1.2
# Push to registry
docker push myregistry.azurecr.io/team/my-app:v1.2
# List images in a registry (Docker Hub example via API)
curl -s https://hub.docker.com/v2/repositories/library/nginx/tags | jq '.results[].name'
# Logout
docker logout myregistry.azurecr.io

Docker distributes an official registry image you can self-host:

Terminal window
# Run a local registry on port 5000
docker run -d \
-p 5000:5000 \
--name registry \
--restart=always \
-v registry-data:/var/lib/registry \ # persist images
registry:2
# Push an image to your local registry
docker tag my-app:latest localhost:5000/my-app:latest
docker push localhost:5000/my-app:latest
# Pull from your local registry
docker pull localhost:5000/my-app:latest

For a production private registry, add:

  • TLS: Use a reverse proxy (Nginx, Caddy) with a valid certificate. Docker requires HTTPS for non-localhost registries.
  • Authentication: Basic auth via htpasswd file, or proxy through an OAuth provider.
  • Vulnerability scanning: Use Harbor or Trivy integration.
Image ReferenceResolved To
nginxdocker.io/library/nginx:latest
nginx:alpinedocker.io/library/nginx:alpine
myuser/myappdocker.io/myuser/myapp:latest
myregistry.io/myapp:v2myregistry.io/myapp:v2
myapp@sha256:abc123Content-addressed, exact digest
  • Digest pinning: sha256:... digests are immutable. Unlike tags, they cannot be overwritten. Use digests in production for reproducible deployments.
  • Tags are mutable. The image behind nginx:latest changes every time a new release is published. Never rely on latest in CI/CD.
  • Content Trust: Set DOCKER_CONTENT_TRUST=1 to enforce that only cryptographically signed images are pulled. Requires the image publisher to sign with Docker Notary. Not widely adopted outside official images, but useful in high-security environments.
  • Unauthenticated pulls: 100 pulls per 6 hours per IP.
  • Authenticated free tier: 200 pulls per 6 hours per account.
  • Paid plans: unlimited.

For CI/CD pipelines that frequently pull from Docker Hub, either:

  1. Authenticate your CI runner with a Docker Hub account.
  2. Mirror Docker Hub through a private registry (pull-through cache) to avoid rate limits hitting your entire team.
Terminal window
# Configure a Docker Hub pull-through mirror in /etc/docker/daemon.json
cat > /etc/docker/daemon.json <<'EOF'
{
"registry-mirrors": ["https://my-dockerhub-mirror.mycompany.io"]
}
EOF
# Restart Docker for the change to take effect
systemctl restart docker