TL;DR #
- GitHub Packages: Free for public repos, integrates with Actions. Start here.
- Docker Hub: Still viable for public images. Private repos get expensive fast.
- ECR/GCR/ACR: Use if already on AWS/GCP/Azure. Pricing scales with usage.
- Harbor: Self-host only if you need vulnerability scanning or multi-tenancy.
- Don’t self-host unless you have compliance requirements or enjoy midnight registry outages.
What is a Container Registry? #
A container registry stores your Docker images. Think Git for containers.
You push images after building. Your deployment tools pull them. Simple concept, endless implementation choices.
For small teams, the decision boils down to: use someone else’s infrastructure or run your own. Spoiler: use someone else’s.
Who Should Use What #
GitHub Packages if:
- Your code lives in GitHub
- You use GitHub Actions for CI
- You want zero additional vendors
Docker Hub if:
- You publish open source images
- You need the widest compatibility
- You’re OK with their pricing for private repos
Cloud Provider Registry (ECR/GCR/ACR) if:
- You’re already all-in on AWS/GCP/Azure
- You deploy to the same cloud
- You want IAM integration
Harbor (self-hosted) if:
- Compliance requires on-premises storage
- You need vulnerability scanning without paying
- You have spare ops capacity
Detailed Comparison #
GitHub Packages #
Free for public repos. 500MB storage + 1GB transfer/month free for private. After that: $0.25/GB storage, $0.50/GB transfer.
Pros:
- Integrated with GitHub Actions
- Same auth as your repos
- Decent free tier
Cons:
- Tied to GitHub ecosystem
- Limited features vs dedicated registries
- Transfer costs add up
Docker Hub #
One private repo free. Team plan: $7/user/month for unlimited private repos.
Pros:
- The default registry
- Great for public images
- Wide tool support
Cons:
- Rate limits on pulls (100/6hr anonymous, 200/6hr authenticated)
- Gets expensive for private repos
- Recent pricing changes upset many
ECR (AWS) #
$0.10/GB/month storage. $0.09/GB transfer (to internet). Free transfer within AWS region.
Pros:
- IAM integration
- Lifecycle policies
- Vulnerability scanning included
Cons:
- AWS-only
- No web UI worth mentioning
- Complexity for simple needs
Harbor #
Free (self-hosted). Your infrastructure costs.
Pros:
- Full control
- Built-in vulnerability scanning
- Multi-tenancy support
Cons:
- You maintain it
- Needs 4GB RAM minimum
- Another thing to monitor
Quick Setup Examples #
GitHub Packages #
# .github/workflows/build.yml
name: Build and Push
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
permissions:
packages: write
steps:
- uses: actions/checkout@v4
- name: Login to GitHub Container Registry
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:
push: true
tags: ghcr.io/${{ github.repository }}:latestECR with Terraform #
# ecr.tf
resource "aws_ecr_repository" "app" {
name = "my-app"
image_scanning_configuration {
scan_on_push = true
}
}
resource "aws_ecr_lifecycle_policy" "app" {
repository = aws_ecr_repository.app.name
policy = jsonencode({
rules = [{
rulePriority = 1
description = "Keep last 10 images"
selection = {
tagStatus = "any"
countType = "imageCountMoreThan"
countNumber = 10
}
action = {
type = "expire"
}
}]
})
}
output "repository_url" {
value = aws_ecr_repository.app.repository_url
}Cost Comparison (10-Person Team) #
Assuming 50GB storage, 200GB transfer/month:
| Registry | Monthly Cost | Notes |
|---|---|---|
| GitHub Packages | ~$112 | $12.50 storage + $100 transfer |
| Docker Hub | $70 | Team plan, unlimited storage/transfer |
| ECR | ~$23 | $5 storage + $18 transfer (external) |
| Harbor | ~$40-80 | VM/storage costs |
ECR wins on price if you’re transferring within AWS. GitHub Packages works if you stay under limits. Docker Hub is predictable.
Operations Considerations #
Availability: Cloud registries handle this. Harbor needs redundancy planning.
Access Control: All support tokens/service accounts. Cloud registries integrate with IAM.
Cleanup: Old images eat money. Set lifecycle policies. ECR and Harbor support this natively. GitHub Packages and Docker Hub need external scripts.
Monitoring: Cloud registries give basic metrics. Harbor needs Prometheus setup.
Security Notes #
All registries support image signing and vulnerability scanning. Costs vary:
- GitHub Packages: Via Actions (free minutes permitting)
- Docker Hub: Paid feature
- ECR: Included
- Harbor: Included (uses Trivy)
For small teams, ECR’s included scanning often tips the balance if you’re on AWS.
When to Skip Container Registries #
If you deploy to a single server and rebuild infrequently, you might not need a registry. Build locally, save to tar, scp to server.
This breaks down quickly with multiple developers or environments. But for that weekend project? YAGNI.
Migration Path #
Start with GitHub Packages or your cloud provider. Migrate only when you hit real limitations.
Moving registries is straightforward:
- Pull all images from old registry
- Re-tag for new registry
- Push to new registry
- Update all deployment configs
- Keep old registry read-only for 30 days
The hard part is updating all the references in your CI/CD pipelines and deployment manifests.
Recommendation Matrix #
- Solo project: Docker Hub free tier
- Small team, GitHub users: GitHub Packages
- Small team, AWS infrastructure: ECR
- Compliance requirements: Harbor
- Multi-cloud: Docker Hub paid or Harbor
Start simple. You can always migrate when your needs crystalize.
Related Reads #
- CI/CD for Small Teams - Connecting your registry to pipelines
- GitOps with ArgoCD for Small Teams - Pull-based deployments from registries
- Minimal DevOps Stack - Where registries fit in your infrastructure