Container Registries: Docker Hub, ECR, GCR
Container registries store and distribute Docker images across your infrastructure. They're the artifact repositories of the containerized world, serving the same purpose as npm for JavaScript or...
Key Insights
- Docker Hub works well for public projects and small teams, but cloud-native registries (ECR, GCR) offer superior integration, security, and cost-effectiveness for production workloads within their respective ecosystems.
- Authentication patterns differ significantly: Docker Hub uses simple username/password, ECR leverages IAM with temporary tokens, and GCR uses service account keys—choose based on your existing identity infrastructure.
- Image lifecycle policies and automated cleanup are critical for controlling storage costs; ECR and Artifact Registry provide built-in policy engines while Docker Hub requires manual management or third-party tools.
Understanding Container Registries
Container registries store and distribute Docker images across your infrastructure. They’re the artifact repositories of the containerized world, serving the same purpose as npm for JavaScript or Maven for Java. Every docker pull command hits a registry, whether you realize it or not.
The choice of registry impacts your deployment speed, security posture, and monthly cloud bill. Public registries work for open-source projects and development, but production systems demand private registries with access controls, vulnerability scanning, and geographic distribution.
Docker Hub: The Public Standard
Docker Hub remains the default registry for most developers. When you run docker pull nginx, you’re pulling from Docker Hub’s public repository. It’s the GitHub of container images—ubiquitous, well-documented, and packed with official images.
The free tier provides unlimited public repositories and one private repository. Paid plans start at $5/month for additional private repos, automated builds, and parallel builds. For small teams or open-source projects, this pricing is reasonable.
# Basic authentication
docker login
# Username: youruser
# Password: ********
# Tagging for Docker Hub
docker tag myapp:latest youruser/myapp:1.0.0
# Pushing to Docker Hub
docker push youruser/myapp:1.0.0
# Pulling official images (no prefix needed)
docker pull postgres:15
# Pulling user images (requires username prefix)
docker pull youruser/myapp:1.0.0
Docker Hub’s automated builds connect to GitHub or Bitbucket, rebuilding images on every commit. This feature works well for public projects but lacks the sophistication of modern CI/CD platforms.
# Dockerfile with build args for versioning
FROM node:18-alpine
ARG BUILD_DATE
ARG VERSION
LABEL build_date=$BUILD_DATE
LABEL version=$VERSION
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
CMD ["node", "server.js"]
The vulnerability scanning on paid plans catches known CVEs in your base images and dependencies. However, it’s less comprehensive than dedicated security tools like Snyk or Aqua Security.
Docker Hub’s main weakness is rate limiting. Anonymous users get 100 pulls per 6 hours. Authenticated free users get 200. This becomes painful in CI/CD environments where builds might pull base images dozens of times daily.
AWS Elastic Container Registry (ECR)
ECR integrates seamlessly with AWS services. If you’re running ECS, EKS, or Lambda containers, ECR is the obvious choice. Authentication uses IAM, eliminating password management entirely.
# Authenticate Docker to ECR (token valid 12 hours)
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 \
--image-scanning-configuration scanOnPush=true \
--region us-east-1
# 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
Infrastructure as Code with Terraform makes ECR repositories reproducible:
resource "aws_ecr_repository" "app" {
name = "myapp"
image_tag_mutability = "MUTABLE"
image_scanning_configuration {
scan_on_push = true
}
encryption_configuration {
encryption_type = "KMS"
kms_key = aws_kms_key.ecr.arn
}
}
resource "aws_ecr_lifecycle_policy" "app" {
repository = aws_ecr_repository.app.name
policy = jsonencode({
rules = [{
rulePriority = 1
description = "Keep last 10 images"
selection = {
tagStatus = "tagged"
tagPrefixList = ["v"]
countType = "imageCountMoreThan"
countNumber = 10
}
action = {
type = "expire"
}
}]
})
}
Lifecycle policies automatically delete old images, controlling storage costs. This example keeps only the last 10 tagged versions, expiring everything else. You can create sophisticated policies based on age, count, or tag patterns.
Cross-region replication ensures images are available close to your compute resources, reducing pull times and improving deployment reliability:
resource "aws_ecr_replication_configuration" "example" {
replication_configuration {
rule {
destination {
region = "eu-west-1"
registry_id = data.aws_caller_identity.current.account_id
}
}
}
}
ECR pricing is straightforward: $0.10/GB per month for storage, plus data transfer costs. There’s no charge for data transfer to EC2, ECS, or Lambda in the same region.
Google Container Registry and Artifact Registry
Google deprecated GCR in favor of Artifact Registry, which supports multiple artifact types beyond containers. If you’re starting fresh on GCP, use Artifact Registry. Existing GCR users have until 2024 to migrate.
# Configure Docker authentication for GCR
gcloud auth configure-docker
# Push to GCR (legacy)
docker tag myapp:latest gcr.io/my-project/myapp:latest
docker push gcr.io/my-project/myapp:latest
# Configure for Artifact Registry
gcloud auth configure-docker us-central1-docker.pkg.dev
# Push to Artifact Registry
docker tag myapp:latest \
us-central1-docker.pkg.dev/my-project/my-repo/myapp:latest
docker push \
us-central1-docker.pkg.dev/my-project/my-repo/myapp:latest
IAM integration provides fine-grained access control. Service accounts should have minimal permissions:
# Grant a service account pull-only access
gcloud artifacts repositories add-iam-policy-binding my-repo \
--location=us-central1 \
--member=serviceAccount:deployer@my-project.iam.gserviceaccount.com \
--role=roles/artifactregistry.reader
# Grant push access for CI/CD
gcloud artifacts repositories add-iam-policy-binding my-repo \
--location=us-central1 \
--member=serviceAccount:ci-builder@my-project.iam.gserviceaccount.com \
--role=roles/artifactregistry.writer
Artifact Registry supports vulnerability scanning through integration with Container Analysis. Enable it per repository:
gcloud artifacts repositories create my-repo \
--repository-format=docker \
--location=us-central1 \
--description="Production application images" \
--vulnerability-scanning=ENABLED
Pricing matches ECR closely: $0.10/GB per month for storage. Network egress within the same region is free, but cross-region or internet egress incurs standard GCP networking charges.
Feature Comparison and Cost Analysis
| Feature | Docker Hub | AWS ECR | GCP Artifact Registry |
|---|---|---|---|
| Free tier | 1 private repo | No (pay per GB) | No (pay per GB) |
| Storage cost | Included in plan | $0.10/GB/month | $0.10/GB/month |
| Vulnerability scanning | Paid plans only | Included | Included |
| Lifecycle policies | No | Yes | Yes |
| Multi-region | Limited | Replication available | Multi-region repos |
| IAM integration | No | Full AWS IAM | Full GCP IAM |
| Rate limits | 200 pulls/6hrs | No limits | No limits |
For a team storing 100GB of images, Docker Hub’s Team plan costs $7/user/month (minimum 5 users = $35/month). ECR costs $10/month plus minimal transfer fees. Artifact Registry costs the same. Cloud-native registries win on cost for most production scenarios.
Best Practices and Security
Tag images with semantic versions, never rely solely on latest:
# Good tagging strategy
docker tag myapp:latest myapp:1.2.3
docker tag myapp:latest myapp:1.2
docker tag myapp:latest myapp:1
# Push all tags
docker push myapp:1.2.3
docker push myapp:1.2
docker push myapp:1
docker push myapp:latest
This allows rollbacks to specific versions while maintaining convenient tags for major/minor versions.
Enable Docker Content Trust for image signing:
export DOCKER_CONTENT_TRUST=1
docker push youruser/myapp:1.0.0
# Prompts for signing key passphrase
Integrate vulnerability scanning in CI/CD pipelines:
# GitHub Actions example
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- name: Scan image
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
severity: CRITICAL,HIGH
exit-code: 1
- name: Push if scan passes
run: |
docker tag myapp:${{ github.sha }} \
$ECR_REGISTRY/myapp:${{ github.sha }}
docker push $ECR_REGISTRY/myapp:${{ github.sha }}
Implement retention policies aggressively. Untagged images from failed builds accumulate quickly:
{
"rules": [{
"rulePriority": 1,
"description": "Delete untagged images after 7 days",
"selection": {
"tagStatus": "untagged",
"countType": "sinceImagePushed",
"countUnit": "days",
"countNumber": 7
},
"action": {
"type": "expire"
}
}]
}
Choosing the Right Registry
Use Docker Hub for open-source projects, public images, and development environments. The ecosystem expects to find popular projects there.
Choose ECR if you’re AWS-native. The IAM integration, VPC endpoints for private connectivity, and tight ECS/EKS integration make it the default choice for AWS deployments.
Select Artifact Registry for GCP workloads or when you need multi-format artifact storage (Maven, npm, Python packages alongside containers).
For multi-cloud deployments, consider running your own registry using Harbor or GitLab Container Registry. This adds operational overhead but provides cloud independence.
Migration between registries is straightforward with tools like skopeo:
# Copy image between registries without pulling locally
skopeo copy \
docker://docker.io/youruser/myapp:1.0.0 \
docker://123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp:1.0.0
Your registry choice should align with your cloud provider, optimize for your team’s workflow, and minimize operational complexity. Don’t overthink it—pick the registry that integrates best with your existing infrastructure and move forward.