Skip to content
ExponaBLOG
AWS FOR DEVELOPERS · DAY 023 / 100

Scan images on push: what every AWS developer should know

Catch vulnerable packages before they ship.

Expona Admin6 min read

Scan images on push: Your first line of defense

You've built a solid CI/CD pipeline, but have you considered what happens after your Docker image is created? Most teams ship images without knowing what's inside—outdated dependencies, unpatched libraries, known vulnerabilities lurking in every layer. Scan images on push gives you automated visibility into those risks before they reach production, catching vulnerable packages before they ship. In this post, I'll show you how to enable scanning in ECR, leverage AWS Inspector for deeper analysis, enforce policy gates in your pipeline, and maintain hygiene through regular base image rebuilds.

Enable scanning in ECR

Amazon ECR (Elastic Container Registry) has built-in scanning capabilities that activate with a few clicks. The basic scanner runs automatically when you push an image, checking for known Common Vulnerabilities and Exposures (CVEs) in your container layers.

Here's how to enable it via the AWS CLI:

bash
aws ecr put-image-scan-config \
  --repository-name my-app \
  --image-scan-config scanOnPush=true \
  --region us-east-1

Once enabled, every push triggers a scan. You'll see results in the ECR console under the image details. The scanner identifies vulnerable packages and their severity levels (critical, high, medium, low).

In practice, I've watched teams enable this and immediately discover they're running Node.js 14 base images with dozens of high-severity CVEs. That immediate feedback loop is invaluable. The scan typically completes in 2–5 minutes, giving you quick turnaround.

You can also trigger manual scans on existing images:

bash
aws ecr start-image-scan \
  --repository-name my-app \
  --image-id imageTag=latest \
  --region us-east-1

Check the scan results programmatically:

bash
aws ecr describe-image-scan-findings \
  --repository-name my-app \
  --image-id imageTag=latest \
  --region us-east-1

Use enhanced scanning with Inspector

The basic ECR scanner works well for common vulnerabilities, but AWS Inspector provides deeper analysis. Inspector scans OS-level packages and can detect runtime behavior issues that a signature-based scanner might miss.

To enable enhanced scanning, update your image scan configuration:

bash
aws ecr put-image-scan-config \
  --repository-name my-app \
  --image-scan-config \
    scanOnPush=true \
    scanFrequency=SCAN_ON_PUSH \
  --encryption-config encryptionType=AES256 \
  --region us-east-1

More importantly, enable Inspector in your AWS account. Inspector provides centralized vulnerability management across your entire container registry:

bash
aws inspector2 enable \
  --resource-types EC2 ECR \
  --region us-east-1

Once enabled, Inspector continuously monitors your ECR images and provides a unified dashboard. You get detailed CVSS scores, affected package versions, remediation paths, and even links to the original CVE documentation.

I've found Inspector particularly useful for tracking image drift. You can see which images in your registry haven't been updated in months, which is often where vulnerabilities hide. It also integrates with EventBridge, so you can trigger Lambda functions or SNS notifications when new vulnerabilities are discovered in your images.

Set up an EventBridge rule to notify your team:

bash
aws events put-rule \
  --name ecr-scan-findings \
  --event-pattern '{
    "source": ["aws.inspector2"],
    "detail-type": ["Inspector2 Finding - Published"]
  }' \
  --state ENABLED

Fail pipelines on critical findings

Scanning is useless if you don't enforce it. The real power comes from making your CI/CD pipeline fail when critical vulnerabilities are detected. This prevents vulnerable images from ever reaching your cluster.

If you're using GitHub Actions, here's a practical approach:

bash
#!/bin/bash
set -e

IMAGE_TAG=$1
CRITICAL_THRESHOLD=0

# Get scan findings
FINDINGS=$(aws ecr describe-image-scan-findings \
  --repository-name my-app \
  --image-id imageTag=$IMAGE_TAG \
  --query 'imageScanFindings.findingSeverityCounts.CRITICAL' \
  --output text)

if [ "$FINDINGS" -gt "$CRITICAL_THRESHOLD" ]; then
  echo "❌ Build failed: $FINDINGS critical vulnerabilities found"
  exit 1
fi

echo "✓ Image scan passed"

Integrate this into your GitHub Actions workflow:

bash
name: Build and Scan

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Build image
        run: docker build -t my-app:${{ github.sha }} .
      
      - name: Push to ECR
        run: |
          aws ecr get-login-password --region us-east-1 | \
            docker login --username AWS --password-stdin $ECR_REGISTRY
          docker push $ECR_REGISTRY/my-app:${{ github.sha }}
      
      - name: Wait for scan
        run: sleep 30
      
      - name: Check scan results
        run: ./scripts/check-scan.sh ${{ github.sha }}
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

I recommend a tiered approach: fail on critical and high-severity findings, but allow deployments with medium findings if they have approved remediation plans. This prevents you from being blocked by low-risk issues while maintaining real security gates.

You can also use AWS Systems Manager to track exceptions. Create approval workflows for medium-severity findings:

bash
aws ssm put-parameter \
  --name /vulnerabilities/approved-exceptions \
  --value '["CVE-2023-12345"]' \
  --type StringList

Rebuild base images regularly

Scanning catches vulnerabilities, but you also need to prevent them from accumulating. The best defense is keeping your base images fresh. Old Alpine or Ubuntu bases accumulate OS-level CVEs that require rebuilds to fix.

Set up a scheduled rebuild process. I typically recommend weekly or bi-weekly rebuilds for production images. Use EventBridge to trigger rebuilds automatically:

bash
aws events put-rule \
  --name weekly-image-rebuild \
  --schedule-expression "cron(0 2 ? * MON *)" \
  --state ENABLED

Connect this to a CodeBuild project:

bash
aws events put-targets \
  --rule weekly-image-rebuild \
  --targets "Id"="1","Arn"="arn:aws:codebuild:us-east-1:123456789012:project/rebuild-images","RoleArn"="arn:aws:iam::123456789012:role/EventBridgeRole"

Your CodeBuild buildspec should pull the latest base image and rebuild:

bash
version: 0.2

phases:
  pre_build:
    commands:
      - echo "Rebuilding images with latest base..."
      - aws ecr get-login-password | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com
  
  build:
    commands:
      - docker pull node:20-alpine
      - docker build -t my-app:latest --build-arg NODE_VERSION=20-alpine .
      - docker tag my-app:latest $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/my-app:latest
  
  post_build:
    commands:
      - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/my-app:latest
      - echo "Rebuild complete"

Monitor which base images have known vulnerabilities using Inspector. Create a dashboard to track this across your portfolio:

bash
aws cloudwatch put-metric-alarm \
  --alarm-name high-severity-images \
  --alarm-description "Alert when images have high-severity CVEs" \
  --metric-name FindingCount \
  --namespace AWS/Inspector2 \
  --statistic Sum \
  --period 300 \
  --threshold 1 \
  --comparison-operator GreaterThanOrEqualToThreshold

Common mistakes

  • Ignoring scan results: Enabling scanning but not enforcing it is theater. I've seen teams scan every image and then deploy anyway. Set clear policy gates.
  • Using outdated base images: Pulling ubuntu:latest and thinking you're current is risky. Pin versions explicitly and rebuild regularly. ubuntu:22.04 today is better than ubuntu:latest in six months.
  • Not accounting for scan latency: ECR scans take 2–5 minutes. If you deploy immediately after push, you bypass the scan entirely. Add delays or use image signing to enforce scanning completion.
  • Focusing only on container layer vulnerabilities: Your application dependencies matter just as much. Pair ECR scanning with dependency scanning tools like Snyk or GitHub's SAST.
  • Never reviewing approved exceptions: When you whitelist a CVE, set a reminder to review it quarterly. What seemed acceptable with a mitigation plan three months ago might now have a patch available.

Key takeaways

  • Enable ECR's basic scanning on push with a single API call—it's free and catches most known vulnerabilities immediately.
  • Use Inspector for deeper, continuous monitoring across your entire registry and integration with your alerting systems.
  • Make scanning enforcement mandatory in CI/CD: fail builds on critical findings and require exceptions to be tracked and reviewed.
  • Rebuild base images weekly or bi-weekly to prevent OS-level CVEs from accumulating; treat this as routine maintenance, not optional.
  • Combine container scanning with application-level dependency scanning and keep audit trails of all approved exceptions for compliance.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles