How to Scan Docker Images for Vulnerabilities with Trivy (and Amazon ECR)
To scan a Docker image for vulnerabilities, install Trivy (free, from Aqua Security) and run trivy image <image>. It lists the packages inside the image and matches them with known CVEs, then prints a table by severity. Use --severity HIGH,CRITICAL --ignore-unfixed to see what you can fix today, and --exit-code 1 to fail a CI build. On AWS, also turn on Amazon ECR scan on push.
Quick answer
trivy image nginx:1.27 # full report
trivy image --severity HIGH,CRITICAL --ignore-unfixed nginx:1.27
trivy image --exit-code 1 --severity CRITICAL myapp:2.4.1 # CI gate
trivy fs --scanners secret,misconfig . # repo: secrets + Dockerfile
A live example first
We compare two versions of the official nginx image on an Ubuntu EC2 instance. First install Trivy (one of the methods from the Trivy documentation):
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy:latest image --severity HIGH,CRITICAL nginx:1.21
Trimmed output:
nginx:1.21 (debian 11.6)
Total: 120 (HIGH: 98, CRITICAL: 22)
┌──────────────┬────────────────┬──────────┬───────────────────┬───────────────┐
│ Library │ Vulnerability │ Severity │ Installed Version │ Fixed Version │
├──────────────┼────────────────┼──────────┼───────────────────┼───────────────┤
│ libwebp6 │ CVE-2023-4863 │ CRITICAL │ 0.6.1-2.1 │ 0.6.1-2.1+deb11u2 │
│ openssl │ CVE-2023-0286 │ HIGH │ 1.1.1n-0+deb11u3 │ 1.1.1n-0+deb11u4 │
│ ... │ │ │ │ │
└──────────────┴────────────────┴──────────┴───────────────────┴───────────────┘
Now the current Alpine-based tag:
trivy image --severity HIGH,CRITICAL nginx:1.27-alpine
nginx:1.27-alpine (alpine 3.20)
Total: 0 (HIGH: 0, CRITICAL: 0)
(Exact counts change every day as new CVEs are published and images are rebuilt. Run it yourself to see today's numbers.)
Friends, look at that difference. Same web server, but the old image carries a critical libwebp bug (CVE-2023-4863, the one used in real attacks against browsers in 2023). The fix was not a code change. It was choosing a fresh, smaller base image.
How does Trivy find vulnerabilities?
Trivy compares the packages inside the image with a CVE database, and CI can block HIGH/CRITICAL.
An image is a stack of layers. Trivy reads them and builds a list of everything installed: Debian or Alpine packages (apt, apk), plus app libraries such as Java .jar files, package-lock.json, requirements.txt, go.sum, Gemfile.lock and composer.lock. It then matches each name and version with its vulnerability database, which is built from sources like the NVD and the Debian, Alpine and GitHub security advisories.
How do I scan step by step?
- Install Trivy (apt or rpm repo,
brew install trivy, or the Docker image). - Run
trivy image <name:tag>. - Read the Total line first.
- Re-run with
--severity HIGH,CRITICAL --ignore-unfixed. - For each row, note the Fixed Version.
- Upgrade the base image or the library, then rebuild.
- Scan again and compare the totals.
- Add the scan to CI with
--exit-code 1.
What Trivy finds in each language
| Stack | File Trivy reads | Example finding |
|---|---|---|
| Java / Spring | .jar files in the image |
log4j-core 2.14 → CVE-2021-44228 (Log4Shell) |
| Node.js | package-lock.json, node_modules |
old lodash → prototype pollution CVEs |
| Python | requirements.txt, site-packages |
old urllib3 / requests CVEs |
| Go | Go binary build info, go.sum |
old golang.org/x/net HTTP/2 CVEs |
| PHP | composer.lock |
old guzzlehttp/guzzle CVEs |
| Ruby | Gemfile.lock |
old nokogiri CVEs |
| .NET | *.deps.json |
old System.Text.Encodings.Web CVEs |
| OS layer | dpkg / apk / rpm database | openssl, glibc, zlib, libwebp |
Add Trivy to a CI pipeline
GitHub Actions
- name: Scan image
uses: aquasecurity/trivy-action@0.24.0
with:
image-ref: myapp:${{ github.sha }}
severity: HIGH,CRITICAL
ignore-unfixed: true
exit-code: '1'
GitLab CI or Jenkins (shell step)
trivy image --exit-code 1 --severity CRITICAL --ignore-unfixed myapp:$BUILD_NUMBER
Start with CRITICAL only, so the team is not flooded, then add HIGH once the backlog is clean.
Scan more than images
trivy fs --scanners vuln,secret,misconfig . # your source folder
trivy config k8s/ # Kubernetes YAML
trivy config terraform/ # Terraform
trivy config warns about things like containers running as root, missing resource limits and S3 buckets without encryption.
Scan in Amazon ECR
- Open ECR → your repository → Edit.
- Turn on Scan on push (basic scanning).
- For deeper checks, go to ECR → Private registry → Scanning configuration → Enhanced scanning.
- Enhanced scanning uses Amazon Inspector, includes language libraries, and re-scans when new CVEs come out.
- View findings in ECR or in Amazon Inspector.
Google Artifact Registry and Azure Container Registry (with Microsoft Defender for Containers) offer the same kind of scanning.
Real incidents this would have caught
- Log4Shell (CVE-2021-44228), December 2021: teams with image scanning could find every image with a vulnerable
log4j-corein minutes. Teams without it searched servers by hand for days. - xz-utils backdoor (CVE-2024-3094), March 2024: a backdoor in
xzversions 5.6.0 and 5.6.1 was found by a Microsoft engineer before it spread widely. Scanners quickly added the CVE, and teams could prove their images used safe versions.
Mistakes to avoid
- Scanning once and forgetting. New CVEs appear every day. Re-scan running images on a schedule, or use ECR enhanced scanning.
- Failing on every LOW finding. Developers start ignoring the scanner. Gate on what matters.
- Treating zero findings as "secure". A scanner only knows published issues.
Summary table
| Task | Command |
|---|---|
| Scan an image | trivy image nginx:1.27 |
| Only serious, fixable | --severity HIGH,CRITICAL --ignore-unfixed |
| Fail CI | --exit-code 1 |
| Secrets + config in repo | trivy fs --scanners secret,misconfig . |
| Kubernetes / Terraform | trivy config <folder> |
| Registry scanning | ECR scan on push / enhanced scanning |
Quick quiz: check what you learned
What does Trivy compare an image against?
- A database of known CVEs
- The Docker Hub star ratings
- The kubelet runtime logs
- The EC2 security group rules
Show answer
A. Trivy lists packages in the image and matches them with published CVEs.
Which flag makes a CI job fail when issues are found?
- --exit-code 1
- --format table
- --quiet mode on
- --cache-dir /tmp
Show answer
A. With --exit-code 1, Trivy returns failure when findings match your filters.
What does --ignore-unfixed hide from the report?
- CVEs with no fix available
- All of the LOW findings
- Secrets found in the layers
- Misconfigured Kubernetes YAML
Show answer
A. It removes findings that cannot be fixed by upgrading yet.
Which ECR option re-scans images when new CVEs appear?
- Enhanced scanning (Inspector)
- Basic scan on push only
- Lifecycle policy rules
- Cross-region replication
Show answer
A. Enhanced scanning keeps checking images continuously with Amazon Inspector.
Learn it properly
This guide is the short path. These pages go deeper, with labs:
Frequently asked questions
What is Trivy?
Trivy is a free, open-source scanner from Aqua Security. It finds known vulnerabilities (CVEs) in OS packages and app libraries inside images, plus secrets and misconfigurations.
Does Trivy need internet access?
It downloads a vulnerability database on first run and updates it every few hours. For offline use you can mirror the database.
What does --ignore-unfixed do?
It hides vulnerabilities that have no fixed version yet, so the report shows only what you can fix today.
Is a scan with zero CVEs proof the image is safe?
No. A scanner only finds known, published issues. You still need least privilege, non-root users and patching.
How is ECR scanning different?
Basic ECR scanning checks OS packages on push. Enhanced scanning uses Amazon Inspector, covers language libraries and re-scans when new CVEs come out.
Can I scan Kubernetes YAML and Terraform?
Yes. trivy config . checks Dockerfiles, Kubernetes manifests and Terraform for risky settings.