DEV Community

Cover image for AWS ECR in 2026: Pull, Inspect, Scan & Automate Docker Images: Complete Guide
Pooya Golchian
Pooya Golchian

Posted on • Originally published at pooya.blog

AWS ECR in 2026: Pull, Inspect, Scan & Automate Docker Images: Complete Guide

AWS Elastic Container Registry (ECR) is the default private Docker registry for AWS workloads. Teams interact with it dozens of times a day, but most only know the basics. This guide covers the full workflow: authenticate securely, pull and inspect images, extract filesystem layers without running a container, scan for CVEs with Amazon Inspector v2, manage costs through lifecycle policies, and automate everything with GitHub Actions OIDC.

Prerequisites

  • AWS CLI v2 installed and configured (aws configure)
  • Docker Engine ≥ 24 running locally
  • IAM user or role with ECR read permissions

Step 1: Authenticate to ECR

ECR uses short-lived tokens (12-hour TTL) tied to your AWS identity. Authenticate before any pull or push:

# Replace <region> and <account-id> with your actual values
aws ecr get-login-password --region us-east-1 \
  | docker login --username AWS --password-stdin \
    123456789012.dkr.ecr.us-east-1.amazonaws.com
Enter fullscreen mode Exit fullscreen mode

In automation, use the aws-actions/amazon-ecr-login GitHub Action (Step 7) instead of running this manually.

Step 2: Find Available Tags

# List all image tags in a repository
aws ecr list-images \
  --repository-name my-repo \
  --query 'imageIds[?imageTag!=null].imageTag' \
  --output table
Enter fullscreen mode Exit fullscreen mode

Step 3: Pull the Image

docker pull 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-repo:latest
Enter fullscreen mode Exit fullscreen mode

Step 4: Inspect & Run Commands in the Image

Run a one-off command inside the image without a persistent container:

# List root filesystem
docker run --rm \
  123456789012.dkr.ecr.us-east-1.amazonaws.com/my-repo:latest \
  ls -la /

# Inspect baked-in environment variables
docker run --rm \
  123456789012.dkr.ecr.us-east-1.amazonaws.com/my-repo:latest \
  env

# Override entrypoint for interactive exploration
docker run --rm -it \
  --entrypoint /bin/sh \
  123456789012.dkr.ecr.us-east-1.amazonaws.com/my-repo:latest
Enter fullscreen mode Exit fullscreen mode

Step 5: Extract the Full Filesystem Without Running the Container

Useful for recovering lost Dockerfiles, auditing third-party images, or forensic investigation:

# Save the image as a tar archive
docker image save \
  123456789012.dkr.ecr.us-east-1.amazonaws.com/my-repo:latest \
  > my-image.tar

# Extract into a directory
mkdir -p image-fs && tar -xf my-image.tar -C image-fs

# Read the layer manifest (shows layer order)
cat image-fs/manifest.json | python3 -m json.tool

# Extract a specific layer to inspect its files
mkdir layer0 && tar -xf image-fs/<layer-digest>/layer.tar -C layer0

# Search for env files or config across all layers
find image-fs -name "*.env" -o -name "Dockerfile" 2>/dev/null
Enter fullscreen mode Exit fullscreen mode

Each layer corresponds to a RUN, COPY, or ADD instruction in the original Dockerfile. Stacking them in manifest order reconstructs the final container filesystem.

Step 6: Security: Scan with Amazon Inspector v2

Amazon Inspector v2 continuously monitors ECR images for CVEs, scanning on push and re-scanning when new vulnerabilities are published.

Enable Enhanced Scanning

# Enable Inspector v2 for ECR
aws inspector2 enable --resource-types ECR

# Configure continuous enhanced scanning for all repos
aws ecr put-registry-scanning-configuration \
  --scan-type ENHANCED \
  --rules '[{
    "repositoryFilters": [{"filter": "*", "filterType": "WILDCARD"}],
    "scanFrequency": "CONTINUOUS_SCAN"
  }]'
Enter fullscreen mode Exit fullscreen mode

View Findings

aws inspector2 list-findings \
  --filter-criteria '{
    "ecrImageRepositoryName": [{"comparison": "EQUALS", "value": "my-repo"}]
  }' \
  --query 'findings[*].{
    Severity: severity,
    CVE: packageVulnerabilityDetails.vulnerabilityId,
    Package: packageVulnerabilityDetails.vulnerablePackages[0].name
  }' \
  --output table
Enter fullscreen mode Exit fullscreen mode

Align remediation with severity: patch CRITICAL and HIGH immediately; schedule MEDIUM for the next sprint.

Step 7: Lifecycle Policies: Control Storage Costs

Without policies, ECR silently accumulates thousands of images. One lifecycle rule pays for itself immediately:

aws ecr put-lifecycle-policy \
  --repository-name my-repo \
  --lifecycle-policy-text '{
    "rules": [
      {
        "rulePriority": 1,
        "description": "Keep last 10 versioned releases",
        "selection": {
          "tagStatus": "tagged",
          "tagPrefixList": ["v"],
          "countType": "imageCountMoreThan",
          "countNumber": 10
        },
        "action": { "type": "expire" }
      },
      {
        "rulePriority": 2,
        "description": "Expire untagged images after 7 days",
        "selection": {
          "tagStatus": "untagged",
          "countType": "sinceImagePushed",
          "countUnit": "days",
          "countNumber": 7
        },
        "action": { "type": "expire" }
      }
    ]
  }'
Enter fullscreen mode Exit fullscreen mode

Step 8: Full CI/CD Pipeline with GitHub Actions (OIDC)

Use OIDC federation, no static AWS credentials stored in GitHub Secrets:

# .github/workflows/ecr-deploy.yml
name: Build & Push to ECR

on:
  push:
    branches: [main]

permissions:
  id-token: write   # Required for OIDC
  contents: read

jobs:
  build-push:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS credentials via OIDC
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-ecr-push
          aws-region: us-east-1

      - name: Login to ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Build, tag & push
        env:
          REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build -t $REGISTRY/my-repo:$IMAGE_TAG .
          docker build -t $REGISTRY/my-repo:latest .
          docker push $REGISTRY/my-repo:$IMAGE_TAG
          docker push $REGISTRY/my-repo:latest
Enter fullscreen mode Exit fullscreen mode

The OIDC role should have only the minimum ECR permissions: no full AmazonEC2ContainerRegistryFullAccess, scope it to ecr:GetAuthorizationToken + push operations on your specific repositories.

Quick Reference

Task Command
Authenticate `aws ecr get-login-password \
List tags {% raw %}aws ecr list-images --repository-name <repo> --query 'imageIds[?imageTag!=null].imageTag'
Pull image docker pull <ecr-url>/<repo>:<tag>
Run command docker run --rm --entrypoint /bin/sh <image>
Extract FS docker image save <image> > img.tar && tar -xf img.tar
View CVEs aws inspector2 list-findings ...
Set lifecycle aws ecr put-lifecycle-policy ...

With Inspector v2 scanning every image on push and lifecycle policies preventing storage sprawl, ECR becomes a fully governed container supply chain, not just a registry.

Top comments (0)