Tarek Cheikh
Founder & AWS Security Expert
Amazon Elastic Container Registry (ECR) is the fully managed container registry at the heart of every AWS container workload. Whether you deploy to ECS, EKS, or Lambda, ECR stores and distributes the container images that become your running applications. A compromised or misconfigured registry can undermine your entire container supply chain -- an attacker who can push a poisoned image to ECR effectively controls what code runs in production.
The container supply chain has become a top target. In 2025, AWS enhanced ECR with managed image signing, ECR-to-ECR pull through cache support, archive storage classes, and expanded Inspector scanning that now covers scratch, distroless, and Chainguard base images. Amazon Inspector also added image usage status tracking, showing which images are actively running on ECS and EKS clusters so teams can prioritize vulnerability remediation for images that actually matter.
This guide covers 12 security best practices for ECR with real AWS CLI commands, directly aligned with AWS Security Hub controls [ECR.1], [ECR.2], and [ECR.3]. For a quick overview of ECR security posture, see our ECR security card.
Security Hub Control: [ECR.1] -- ECR private repositories should have image scanning configured. Enhanced scanning, powered by Amazon Inspector, goes far beyond basic scanning by providing continuous, automated vulnerability detection for both operating system packages and programming language dependencies (Python, Java, Node.js, Go, .NET, and more).
As of early 2026, Inspector supports scanning of minimal container bases including scratch, distroless (Debian/Ubuntu-based), and Chainguard images, along with expanded ecosystem coverage for Go toolchains, Oracle JDK, Amazon Corretto, Apache Tomcat, and WordPress. Enhanced scanning also surfaces image usage status -- which clusters are running each image, when it was last pulled, and how many clusters reference it -- so you can focus remediation on actively deployed images.
# Enable enhanced scanning at the registry level
aws ecr put-registry-scanning-configuration \
--scan-type ENHANCED \
--rules '[{"scanFrequency":"CONTINUOUS_SCAN","repositoryFilters":[{"filter":"*","filterType":"WILDCARD"}]}]'
# Verify the scanning configuration
aws ecr get-registry-scanning-configuration
# Check scan findings for a specific image
aws ecr describe-image-scan-findings \
--repository-name my-app \
--image-id imageTag=latest
# List images with CRITICAL or HIGH vulnerabilities using Inspector
aws inspector2 list-findings \
--filter-criteria '{
"resourceType": [{"comparison":"EQUALS","value":"AWS_ECR_CONTAINER_IMAGE"}],
"severity": [{"comparison":"EQUALS","value":"CRITICAL"}]
}'
For production environments, configure EventBridge rules to alert on new CRITICAL or HIGH findings so your team can respond before vulnerable images reach production.
Security Hub Control: [ECR.2] -- ECR private repositories should have tag immutability configured. Without immutable tags, an attacker (or even a misconfigured CI pipeline) can overwrite an existing image tag such as v1.2.3 with a completely different image. Every service pulling that tag would then run the replaced image without any warning. Immutable tags prevent this by returning an ImageTagAlreadyExistsException if anyone attempts to push an image with a tag that already exists in the repository.
# Create a new repository with immutable tags
aws ecr create-repository \
--repository-name my-app \
--image-tag-mutability IMMUTABLE
# Update an existing repository to use immutable tags
aws ecr put-image-tag-mutability \
--repository-name my-app \
--image-tag-mutability IMMUTABLE
# Verify the setting
aws ecr describe-repositories \
--repository-names my-app \
--query 'repositories[0].imageTagMutability'
With immutable tags enabled, your CI/CD pipeline must generate unique tags for every build (e.g., commit SHA, build number, or semantic version). Never rely on mutable tags like latest in production deployments.
Security Hub Control: [ECR.3] -- ECR repositories should have at least one lifecycle policy configured. Without lifecycle policies, repositories accumulate images indefinitely, increasing storage costs and expanding your attack surface with stale, potentially vulnerable images. Lifecycle policies automate cleanup by expiring images based on age, count, or tag status.
# Apply a lifecycle policy that keeps only the last 30 tagged images
# and removes untagged images older than 1 day
aws ecr put-lifecycle-policy \
--repository-name my-app \
--lifecycle-policy-text '{
"rules": [
{
"rulePriority": 1,
"description": "Remove untagged images after 1 day",
"selection": {
"tagStatus": "untagged",
"countType": "sinceImagePushed",
"countUnit": "days",
"countNumber": 1
},
"action": { "type": "expire" }
},
{
"rulePriority": 2,
"description": "Keep only last 30 tagged images",
"selection": {
"tagStatus": "tagged",
"tagPrefixList": ["v"],
"countType": "imageCountMoreThan",
"countNumber": 30
},
"action": { "type": "expire" }
}
]
}'
# Preview what the policy would delete before applying
aws ecr get-lifecycle-policy-preview \
--repository-name my-app
ECR now also supports an archive storage class for rarely accessed images. You can use lifecycle rules to automatically transition images to archive storage based on usage patterns, reducing costs while retaining images for compliance requirements. Note that archived images must remain in archive for a minimum of 90 days before deletion.
By default, ECR encrypts images at rest using Amazon S3-managed keys (SSE-S3). While this provides baseline encryption, customer-managed KMS keys give you full control over key rotation, access policies, and audit trails through CloudTrail. This is often required for compliance frameworks like PCI DSS, HIPAA, and FedRAMP.
# Create a KMS key for ECR encryption
aws kms create-key \
--description "ECR repository encryption key" \
--key-usage ENCRYPT_DECRYPT \
--origin AWS_KMS
# Create a repository with KMS encryption
aws ecr create-repository \
--repository-name my-secure-app \
--encryption-configuration '{
"encryptionType": "KMS",
"kmsKey": "arn:aws:kms:us-east-1:111122223333:key/your-key-id"
}' \
--image-tag-mutability IMMUTABLE
# Verify encryption configuration
aws ecr describe-repositories \
--repository-names my-secure-app \
--query 'repositories[0].encryptionConfiguration'
Important: encryption type cannot be changed after repository creation. Plan your encryption strategy before creating repositories. Use repository creation templates to enforce KMS encryption for all new repositories created via pull through cache or replication.
ECR repository policies provide resource-based access control at the repository level. Without explicit policies, repositories inherit broad permissions from IAM identity policies, which can lead to unauthorized image pulls or pushes. A well-crafted repository policy restricts who can push, pull, and manage images within each repository.
# Set a repository policy that allows only specific roles to push
# and restricts pull access to specific accounts
aws ecr set-repository-policy \
--repository-name my-app \
--policy-text '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowPushFromCICD",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111122223333:role/cicd-pipeline-role"
},
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:CompleteLayerUpload",
"ecr:InitiateLayerUpload",
"ecr:PutImage",
"ecr:UploadLayerPart"
]
},
{
"Sid": "AllowPullFromECS",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111122223333:role/ecs-task-execution-role"
},
"Action": [
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer"
]
}
]
}'
# Verify the repository policy
aws ecr get-repository-policy --repository-name my-app
For defense in depth, combine repository policies with IAM identity policies and VPC endpoint policies. Use condition keys like aws:SourceVpc or aws:SourceVpce to restrict access to specific VPCs or endpoints.
By default, ECR API calls and Docker image pulls traverse the public internet. VPC interface endpoints (powered by AWS PrivateLink) keep all ECR traffic within the AWS network, eliminating exposure to the public internet and reducing data transfer costs. This is essential for workloads in private subnets that have no internet gateway or NAT device.
# Create the ECR API endpoint (for describe, create, list operations)
aws ec2 create-vpc-endpoint \
--vpc-id vpc-0123456789abcdef0 \
--service-name com.amazonaws.us-east-1.ecr.api \
--vpc-endpoint-type Interface \
--subnet-ids subnet-aaa111 subnet-bbb222 \
--security-group-ids sg-0123456789abcdef0 \
--private-dns-enabled
# Create the ECR Docker endpoint (for push/pull operations)
aws ec2 create-vpc-endpoint \
--vpc-id vpc-0123456789abcdef0 \
--service-name com.amazonaws.us-east-1.ecr.dkr \
--vpc-endpoint-type Interface \
--subnet-ids subnet-aaa111 subnet-bbb222 \
--security-group-ids sg-0123456789abcdef0 \
--private-dns-enabled
# Create an S3 Gateway endpoint (required -- ECR stores image layers in S3)
aws ec2 create-vpc-endpoint \
--vpc-id vpc-0123456789abcdef0 \
--service-name com.amazonaws.us-east-1.s3 \
--vpc-endpoint-type Gateway \
--route-table-ids rtb-0123456789abcdef0
# Verify the endpoints are available
aws ec2 describe-vpc-endpoints \
--filters "Name=service-name,Values=com.amazonaws.us-east-1.ecr.*" \
--query 'VpcEndpoints[*].{Service:ServiceName,State:State}'
Ensure the security group attached to the VPC endpoints allows inbound TCP traffic on port 443 from your private subnets. For ECS Fargate tasks, add condition keys to the task execution role that restrict ECR access to the specific VPC endpoint using aws:sourceVpce.
Image signing establishes a cryptographic chain of trust from build to deployment. Without signing, there is no way to verify that an image in ECR was actually produced by your CI/CD pipeline and has not been tampered with. ECR now offers managed signing, which automatically signs images during push using AWS Signer -- no need to install Notation or Cosign tooling in your pipeline.
# Create an AWS Signer signing profile for container images
aws signer put-signing-profile \
--profile-name ecr-container-signing \
--platform-id Notation-OCI-SHA384-ECDSA
# For signing with Notation CLI and AWS Signer plugin:
# Install the AWS Signer plugin for Notation
notation plugin install --url https://d2hvyiie56hcat.cloudfront.net/linux/amd64/plugin/latest/notation-aws-signer-plugin.zip
# Sign an image
notation sign \
111122223333.dkr.ecr.us-east-1.amazonaws.com/my-app:v1.0.0 \
--plugin com.amazonaws.signer.notation.plugin \
--id arn:aws:signer:us-east-1:111122223333:/signing-profiles/ecr-container-signing
# Verify the signature
notation verify \
111122223333.dkr.ecr.us-east-1.amazonaws.com/my-app:v1.0.0
For EKS deployments, integrate image verification with Kubernetes admission controllers so that only signed images are admitted to the cluster. Cross-account signing is supported -- a central security account can own signing profiles while developer accounts use them to sign images.
Pull through cache rules allow you to cache images from upstream registries (Docker Hub, GitHub Container Registry, Quay, and as of March 2025, other ECR registries) into your private ECR registry. This reduces external dependencies, avoids Docker Hub rate limits, and gives you a controlled copy of third-party images that you can scan before deployment. However, misconfigured pull through caches can introduce unscanned upstream images into your environment.
# Create a pull through cache rule for Docker Hub
aws ecr create-pull-through-cache-rule \
--ecr-repository-prefix docker-hub \
--upstream-registry-url registry-1.docker.io \
--credential-arn arn:aws:secretsmanager:us-east-1:111122223333:secret:dockerhub-creds
# Create a repository creation template to enforce security settings
# on all repositories created by pull through cache
aws ecr create-repository-creation-template \
--prefix "docker-hub/*" \
--applied-for PULL_THROUGH_CACHE \
--image-tag-mutability IMMUTABLE \
--encryption-configuration '{"encryptionType":"KMS","kmsKey":"arn:aws:kms:us-east-1:111122223333:key/your-key-id"}' \
--resource-tags '[{"Key":"source","Value":"pull-through-cache"}]' \
--lifecycle-policy '{
"rules": [{
"rulePriority": 1,
"selection": {"tagStatus":"untagged","countType":"sinceImagePushed","countUnit":"days","countNumber":7},
"action": {"type":"expire"}
}]
}'
# List existing pull through cache rules
aws ecr describe-pull-through-cache-rules
Always pair pull through cache rules with repository creation templates that enforce scanning, immutable tags, encryption, and lifecycle policies. Store upstream registry credentials in AWS Secrets Manager and rotate them regularly.
Multi-account architectures are standard practice on AWS, but cross-account ECR access must be tightly controlled. A common pattern is a central "shared services" account that hosts container images, with workload accounts pulling from it. Overly permissive cross-account policies can allow unauthorized accounts to pull -- or worse, push -- images to your repositories.
# In the source account: set a repository policy allowing cross-account pull
aws ecr set-repository-policy \
--repository-name shared/my-app \
--policy-text '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCrossAccountPull",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::444455556666:root",
"arn:aws:iam::777788889999:root"
]
},
"Action": [
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchCheckLayerAvailability"
],
"Condition": {
"StringEquals": {
"aws:PrincipalOrgID": "o-yourorgid"
}
}
}
]
}'
# In the workload account: authenticate to the source account registry
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin \
111122223333.dkr.ecr.us-east-1.amazonaws.com
Use the aws:PrincipalOrgID condition key to restrict cross-account access to accounts within your AWS Organization. Never grant push access cross-account unless absolutely necessary, and never use wildcard principals ("*") in repository policies.
Individual developers and CI/CD pipelines frequently create repositories on the fly. Without governance, these repositories inherit default settings -- mutable tags, S3-managed encryption, no lifecycle policies, and no scanning. Repository creation templates let you define security baselines that apply automatically to every new repository matching a prefix pattern.
# Create a default template that applies to all new repositories
aws ecr create-repository-creation-template \
--prefix "" \
--applied-for PULL_THROUGH_CACHE REPLICATION \
--description "Default security baseline for all ECR repositories" \
--image-tag-mutability IMMUTABLE \
--encryption-configuration '{"encryptionType":"KMS","kmsKey":"arn:aws:kms:us-east-1:111122223333:key/your-key-id"}' \
--lifecycle-policy '{
"rules": [{
"rulePriority": 1,
"selection": {"tagStatus":"untagged","countType":"sinceImagePushed","countUnit":"days","countNumber":3},
"action": {"type":"expire"}
}]
}'
# List all repository creation templates
aws ecr describe-repository-creation-templates
# Audit existing repositories against the template settings
aws ecr describe-repositories \
--query 'repositories[?imageTagMutability==`MUTABLE`].repositoryName'
If your template uses KMS encryption or resource tags, you must specify a customRoleArn -- an IAM role that ECR assumes when creating repositories on your behalf. Without this role, repository creation will fail silently.
The base image you choose determines the majority of your container's attack surface. A full Ubuntu or Amazon Linux image includes hundreds of packages your application does not need -- each one a potential vulnerability. Security-hardened, minimal base images like distroless, scratch, or Chainguard images dramatically reduce the number of packages and therefore the number of potential CVEs.
# Example: multi-stage build with distroless base
# Build stage
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/server
# Runtime stage -- distroless contains only the runtime, no shell, no package manager
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=builder /app/server /server
USER 65534:65534
ENTRYPOINT ["/server"]
# Scan an image and count vulnerabilities by severity
aws inspector2 list-finding-aggregations \
--aggregation-type REPOSITORY \
--aggregation-request '{
"repositoryAggregation": {
"repositories": [{"comparison":"EQUALS","value":"my-app"}]
}
}'
# Compare vulnerability counts between a full base and distroless base
aws ecr describe-image-scan-findings \
--repository-name my-app \
--image-id imageTag=v1.0.0-full \
--query 'imageScanFindings.findingSeverityCounts'
aws ecr describe-image-scan-findings \
--repository-name my-app \
--image-id imageTag=v1.0.0-distroless \
--query 'imageScanFindings.findingSeverityCounts'
Run containers as non-root users. In your Dockerfile, use USER to set a non-root UID and test that the application works correctly without elevated privileges.
Every ECR API call -- PutImage, GetAuthorizationToken, BatchDeleteImage, SetRepositoryPolicy -- is logged in AWS CloudTrail. Monitoring these events lets you detect unauthorized image pushes, policy changes, or unusual pull patterns. Combine CloudTrail logging with EventBridge rules to trigger automated responses.
# Create an EventBridge rule to alert on image push events
aws events put-rule \
--name ecr-image-push-alert \
--event-pattern '{
"source": ["aws.ecr"],
"detail-type": ["ECR Image Action"],
"detail": {
"action-type": ["PUSH"],
"result": ["SUCCESS"]
}
}'
# Create a rule to detect repository policy changes
aws events put-rule \
--name ecr-policy-change-alert \
--event-pattern '{
"source": ["aws.ecr"],
"detail-type": ["AWS API Call via CloudTrail"],
"detail": {
"eventSource": ["ecr.amazonaws.com"],
"eventName": ["SetRepositoryPolicy","DeleteRepositoryPolicy","PutRegistryPolicy"]
}
}'
# Add an SNS target to the rule
aws events put-targets \
--rule ecr-policy-change-alert \
--targets '[{"Id":"sns-target","Arn":"arn:aws:sns:us-east-1:111122223333:security-alerts"}]'
For high-security environments, create a metric filter in CloudWatch Logs that triggers an alarm when BatchDeleteImage is called outside of lifecycle policy automation, which could indicate an attacker covering their tracks.
These are the most frequently encountered ECR security issues found during security assessments:
"Principal": "*" grants any AWS account access to pull (or push) images from your repository.ubuntu:latest or amazonlinux:latest contain hundreds of unnecessary packages, each a potential vulnerability.| Best Practice | Security Hub Control | Priority | Status |
|---|---|---|---|
| Enable enhanced image scanning (Inspector) | [ECR.1] | Critical | |
| Enable image tag immutability | [ECR.2] | Critical | |
| Configure lifecycle policies | [ECR.3] | High | |
| Encrypt with customer-managed KMS keys | -- | High | |
| Restrict access with repository policies | -- | High | |
| Use VPC endpoints for private access | -- | High | |
| Sign container images | -- | High | |
| Secure pull through cache rules | -- | Medium | |
| Implement least-privilege cross-account access | -- | High | |
| Use repository creation templates | -- | Medium | |
| Use minimal and hardened base images | -- | High | |
| Monitor with CloudTrail and EventBridge | -- | Medium |
Securing Amazon ECR requires a layered approach that spans image supply chain integrity, access control, network isolation, encryption, and continuous monitoring. By implementing these 12 best practices -- starting with the three Security Hub controls [ECR.1], [ECR.2], and [ECR.3] -- you establish a strong security baseline for your container registry that protects every downstream deployment on ECS, EKS, and Lambda. For more container security guidance, see our ECR security card and related guides on ECS security and EKS security.
This article is just the start. Get the full picture with our free whitepaper - 8 chapters covering IAM, S3, VPC, monitoring, agentic AI security, compliance, and a prioritized action plan with 50+ CLI commands.
Comprehensive guide to securing Amazon Elastic Container Service. Covers task role separation, non-root containers, ECR image scanning, secrets management, GuardDuty runtime monitoring, network isolation, ECScape mitigation, and container image signing.
Comprehensive guide to securing Amazon Elastic Kubernetes Service. Covers Pod Identity, RBAC least privilege, Pod Security Standards, network policies, secrets encryption, GuardDuty EKS protection, EKS Auto Mode, and CIS EKS Benchmark compliance.
Comprehensive guide to securing AWS Lambda functions. Covers execution role least privilege, Function URL authentication, VPC placement, code signing, environment variable encryption, Secrets Manager integration, and SnapStart security considerations.