Container Registry — The Central Store for Docker Images
What Is a Container Registry in Simple Terms?
A container registry is a storage and distribution system for Docker images — like npm is for JavaScript packages or PyPI is for Python packages. You build an image on your laptop, push it to a registry, and your production servers pull it from the same registry to run containers.
Without a registry, you would have to copy image files between machines manually. With a registry, every developer and every server pulls from the same source of truth.
+------------------------------------------+| Developer Laptop || docker build -t payment-api:v3.1.0 . || docker push registry.razorpay.in/... |+------------------------------------------+ | | push v+------------------------------------------+| Container Registry || registry.razorpay.in || Stores: payment-api:v3.1.0 || payment-api:v3.0.0 (old version) || nginx:1.25 |+------------------------------------------+ | | pull v+------------------------------------------+| Production Server / Kubernetes Node || docker pull registry.razorpay.in/... || docker run payment-api:v3.1.0 |+------------------------------------------+Types of Container Registries
+------------------------------------------+| Public Registries || || Docker Hub (hub.docker.com) || Default registry for docker pull || Free public images, rate limited pulls || docker pull nginx = Docker Hub || || GitHub Container Registry (ghcr.io) || Integrated with GitHub Actions || Free for public repos || || Google Container Registry (gcr.io) || Public Google images (distroless) |+------------------------------------------+ +------------------------------------------+| Private Registries (production use) || || AWS ECR (Elastic Container Registry) || Best choice for AWS-based infrastructure|| IAM-based authentication || Lifecycle policies for old images || || GCP Artifact Registry || Best choice for GCP infrastructure || || Azure Container Registry (ACR) || Best choice for Azure infrastructure || || Harbor (self-hosted, open source) || Run your own registry on-premises || Vulnerability scanning built in |+------------------------------------------+Image Naming and Registry URLs
# Full image reference format:# [registry-hostname/][namespace/]repository[:tag][@digest] # Docker Hub (default registry — hostname is optional)nginx # = docker.io/library/nginx:latestnginx:1.25 # = docker.io/library/nginx:1.25myusername/myapp:v1.0 # = docker.io/myusername/myapp:v1.0 # AWS ECR905418385260.dkr.ecr.ap-south-1.amazonaws.com/payment-api:v3.1.0# Format: ACCOUNT_ID.dkr.ecr.REGION.amazonaws.com/REPOSITORY:TAG # GitHub Container Registryghcr.io/myorg/payment-api:v3.1.0 # Self-hosted registryregistry.razorpay.in/payment-api:v3.1.0Working With Registries
# Authenticate to Docker Hubdocker login# Username: myusername# Password: mypassword# Credentials stored at ~/.docker/config.json # Authenticate to AWS ECRaws ecr get-login-password --region ap-south-1 | \ docker login \ --username AWS \ --password-stdin \ 905418385260.dkr.ecr.ap-south-1.amazonaws.com# ECR uses temporary tokens — must re-authenticate every 12 hours # Authenticate to a private registrydocker login registry.razorpay.in # Push an image to a registrydocker build -t payment-api:v3.1.0 .docker tag payment-api:v3.1.0 \ registry.razorpay.in/payment-api:v3.1.0docker push registry.razorpay.in/payment-api:v3.1.0 # Pull an image from a registrydocker pull registry.razorpay.in/payment-api:v3.1.0 # Pull without runningdocker pull nginx:1.25 # List tags for an image in a registry# (Registry API — not a built-in docker command)curl https://registry.razorpay.in/v2/payment-api/tags/listDocker Hub Rate Limits
# Docker Hub limits anonymous pulls:# Anonymous: 100 pulls per 6 hours per IP# Free account: 200 pulls per 6 hours# Pro account: unlimited # On shared build servers (GitHub Actions, Jenkins), many builds# hit the same rate limit — causing pull failures # Fix 1: Authenticate to Docker Hub in CI- name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} # Fix 2: Use a registry mirror to cache Docker Hub images# Configure in /etc/docker/daemon.json:{ "registry-mirrors": ["https://mirror.gcr.io"]}# Google's mirror has no rate limits for Docker Hub imagesECR Lifecycle Policies
Without lifecycle policies, old images accumulate in ECR forever, increasing storage costs:
// ECR lifecycle policy — keep only last 10 images per tag{ "rules": [ { "rulePriority": 1, "description": "Keep last 10 tagged images", "selection": { "tagStatus": "tagged", "tagPrefixList": ["v"], "countType": "imageCountMoreThan", "countNumber": 10 }, "action": { "type": "expire" } }, { "rulePriority": 2, "description": "Remove untagged images after 1 day", "selection": { "tagStatus": "untagged", "countType": "sinceImagePushed", "countUnit": "days", "countNumber": 1 }, "action": { "type": "expire" } } ]}# Apply the lifecycle policyaws ecr put-lifecycle-policy \ --repository-name payment-api \ --lifecycle-policy-text file://lifecycle-policy.json \ --region ap-south-1Troubleshooting Reference
| Problem | Cause | Fix |
|---|---|---|
pull access denied |
Not authenticated | docker login registry-hostname |
unauthorized: authentication required |
Token expired (ECR) | Re-run aws ecr get-login-password |
toomanyrequests: Rate exceeded |
Docker Hub rate limit | Authenticate to Docker Hub or use a mirror |
manifest unknown |
Image tag does not exist | Check exact tag name in registry |
| Slow pulls in CI | Images not cached | Use registry-side caching or pull through cache |
PLACEMENT PRO TIP**Tip:** In GitHub Actions, authenticate to Docker Hub even for public image pulls. Authenticated pulls get 200 pulls per 6 hours per account versus 100 per 6 hours per IP address. On shared GitHub-hosted runners where many organisations share IP addresses, unauthenticated pulls frequently hit rate limits.
REMEMBER THIS**Remember:** Every image you push to a public Docker Hub repository is publicly accessible forever (unless you delete it). If you accidentally push an image containing secrets, rotate the secrets immediately — anyone who pulled that image before deletion has a copy with your credentials.
COMMON MISTAKE / WARNING**Security:** Use private registries for all production images. Public repositories expose your image layers (and any accidentally included secrets) to the entire internet. AWS ECR, GCP Artifact Registry, and self-hosted Harbor all provide private repositories with IAM-based access control.