Container Registries — ECR vs Artifact Registry vs ACR
A container registry is a storage and distribution service for container images. You build an image in CI, push it to a registry, and your runtime environment pulls it when deploying. That sounds simple, but registries sit at a critical junction — if someone tampers with an image between build and deploy, you're running their code in production. Understanding how registries work and how to secure them is not optional.
Container Registry — Tags, Digests & Supply Chain
The Registry API Model
Under the hood, registries implement the OCI Distribution Spec. An image in a registry consists of two things: a manifest (a JSON document describing the image's layers and configuration) and blobs (the actual layer data, content-addressed by SHA256 digest). When you pull an image, your client first fetches the manifest by tag or digest, then downloads any layers it doesn't already have locally.
Tags are mutable pointers — myapp:v1.2.3 points to a specific manifest today, but someone with push access can retag it tomorrow. Digests are immutable — myapp@sha256:abc123... always refers to the exact same content. For production deployments, pin by digest. For human convenience, use tags but understand they can move.
Pull-Through Caches and Mirrors
If every node in your cluster pulls nginx from Docker Hub independently, you're wasting bandwidth and hitting rate limits (Docker Hub throttles anonymous pulls to 100/6h). Pull-through caches proxy requests to upstream registries and cache the layers locally. Most managed registries support this natively — ECR has pull-through cache rules, and you can configure Artifact Registry as a remote repository.
Public vs Private and Authentication
Public registries (Docker Hub, GitHub Container Registry) are fine for open-source base images. Your application images should live in a private registry with authentication tied to your cloud IAM. Each provider handles auth differently: ECR uses short-lived tokens via aws ecr get-login-password, Artifact Registry uses Application Default Credentials or service account keys, and ACR integrates with Azure AD.
Lifecycle Policies
Images accumulate fast. Every CI build pushes a new one. Without cleanup, you're paying for storage that nobody will ever pull again. Lifecycle policies automate deletion based on rules: delete untagged images older than 7 days, keep only the last 10 tagged images per repository, expire images not pulled in 90 days. Set these up on day one — retrofitting cleanup after you have 50,000 images is painful.
Vulnerability Scanning
Registries can scan images for known CVEs in OS packages and language dependencies. Scanning happens at push time and continuously as new vulnerabilities are disclosed. A clean scan today doesn't mean the image is clean next week — new CVEs are published daily.
Integrate scanning into your deployment pipeline: block deploys of images with critical or high vulnerabilities. All three major providers offer built-in scanning, or you can use third-party tools like Trivy, Grype, or Snyk that give you consistent results across providers.
Supply Chain Security
Scanning catches known vulnerabilities, but it doesn't answer a harder question: is this image actually what CI built, or did someone tamper with it? Supply chain security adds cryptographic guarantees.
Image signing with Cosign (part of the Sigstore project) attaches a cryptographic signature to an image digest. Your deployment environment can then enforce a policy: only run images signed by our CI pipeline's key. Attestations go further — they're signed statements about how the image was built (which source commit, which build system, what dependencies were used). SBOMs (Software Bills of Materials) list every component in the image. SLSA (Supply-chain Levels for Software Artifacts) defines maturity levels from L1 (documented build process) to L4 (hermetic, reproducible builds with two-party review).
Admission controllers in Kubernetes (like Kyverno or OPA Gatekeeper) or equivalent policies in managed services can refuse to run unsigned images.
Multi-Arch Images
Modern deployments span architectures — x86 for servers, ARM for cost-efficient instances (Graviton, Tau T2A) or Apple Silicon dev machines. A manifest list (or OCI image index) is a meta-manifest that points to platform-specific images. When you pull nginx:latest, the registry returns the right image for your architecture automatically. Use docker buildx to build multi-arch images in CI.
AWS Implementation (ECR)
ECR provides private repositories per AWS account and region. Authentication uses aws ecr get-login-password piped to docker login, valid for 12 hours. Basic scanning uses Clair; enhanced scanning uses Amazon Inspector for continuous monitoring. Lifecycle policies support rules based on age, count, and tag status. Cross-region and cross-account replication is built in. ECR also supports pull-through caches for Docker Hub, GitHub, and other upstream registries.
GCP Implementation (Artifact Registry)
Artifact Registry replaced the older Container Registry and supports multiple artifact types (containers, Maven, npm, Python). Authentication uses gcloud auth configure-docker or service account keys. Vulnerability scanning uses Container Analysis and can be enforced via Binary Authorization — GCP's admission control that blocks unsigned images. Repositories can be regional or multi-regional. Remote repositories act as pull-through caches.
Azure Implementation (ACR)
ACR integrates with Azure AD for authentication — az acr login handles it. Microsoft Defender for Containers provides vulnerability scanning with continuous monitoring. ACR Tasks can build images directly in Azure without a local Docker daemon. Geo-replication pushes images to multiple regions automatically. Content trust for image signing is supported via Notary v2.
Side-by-Side Comparison
| Aspect | ECR (AWS) | Artifact Registry (GCP) | ACR (Azure) |
|---|---|---|---|
| Auth mechanism | Token via CLI (12h) | ADC / gcloud | Azure AD / az acr login |
| Vulnerability scanning | Inspector (enhanced) | Container Analysis | Defender for Containers |
| Admission control | N/A (use Kyverno on EKS) | Binary Authorization | Azure Policy |
| Multi-arch | Supported | Supported | Supported |
| Lifecycle policies | Rule-based (age, count) | Cleanup policies | Retention policies |
| Replication | Cross-region, cross-account | Multi-region repos | Geo-replication |
| Pull-through cache | Pull-through cache rules | Remote repositories | N/A (mirror manually) |
| Pricing model | Storage + data transfer | Storage + egress | Storage + egress by tier |
Cost Considerations
Registry costs have two components: storage (pennies per GB/month) and egress (data transfer on pulls). Storage is cheap until you have thousands of untagged images piling up. Egress costs depend on where you pull — same-region pulls are free or cheap, cross-region pulls cost real money, and pulling from the internet costs the most. Same-region deployments and lifecycle policies are your main cost levers.
Key Takeaways
- Tags are mutable pointers; digests are immutable content addresses — pin production deployments by digest
- Lifecycle policies prevent unbounded storage growth; set them up when you create the repository, not after
- Vulnerability scanning at push time is the minimum; continuous scanning catches newly disclosed CVEs against existing images
- Supply chain security (signing, attestations, SBOMs) answers "was this image tampered with?" — a question scanning alone cannot
- Pull-through caches reduce bandwidth, avoid rate limits, and speed up pulls across your fleet
- Cross-region replication reduces pull latency but doubles storage costs — only replicate to regions where you actually deploy
Next, we'll take those images out of the registry and actually run them — ECS, Cloud Run, and Container Apps.
Enjoyed this breakdown?
Get new lessons in your inbox.