Skip to content

beginner ~45 min updated 2026-06-01

Trivy Container Scan

Scan container images for CVEs, misconfigurations, and secrets with Trivy. Compare a vulnerable base image against a patched one, generate an SBOM, and gate a CI build on severity.

Objective

Use Trivy to find vulnerabilities and secrets in a container image, fix them by changing the base image, and fail builds on HIGH or CRITICAL findings. You will also produce a CycloneDX SBOM and scan a Dockerfile for misconfigurations.

Prerequisites

  • Docker installed and running
  • Trivy installed (trivy --version works) — install with brew install trivy or curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
  • Basic Dockerfile knowledge
  • Internet access for the first vulnerability DB download (~60 MB)

Architecture

Trivy pulls the target image, unpacks its layers, identifies OS packages and language dependencies, and matches them against its vulnerability database. The same engine also scans for hardcoded secrets and Dockerfile/IaC misconfigurations. Exit codes make it CI-friendly.

+-----------+   pull/unpack   +---------------------+
| Image     | --------------> | Trivy               |
| (registry |                 |  - vuln DB matching |
|  or local)|                 |  - secret rules     |
+-----------+                 |  - misconfig checks |
 Dockerfile  --------------->  |                     |
                              +---------+-----------+
                                        |
                        report (table/json/SBOM) + exit code

Steps

1. Scan a deliberately old image

docker pull python:3.4-alpine
trivy image --severity HIGH,CRITICAL python:3.4-alpine

2. Build an image with a planted secret

mkdir trivy-lab && cd trivy-lab
# Dockerfile
FROM python:3.4-alpine
ENV AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
COPY app.py /app/app.py
USER root
CMD ["python", "/app/app.py"]
echo 'print("hello")' > app.py
docker build -t trivy-lab:vulnerable .

3. Run a full scan: vulnerabilities plus secrets

trivy image --scanners vuln,secret trivy-lab:vulnerable

4. Scan the Dockerfile for misconfigurations

trivy config .

Trivy flags issues such as running as root and missing HEALTHCHECK.

5. Fix the image and rescan

# Dockerfile.fixed
FROM python:3.13-alpine
COPY app.py /app/app.py
RUN adduser -D appuser
USER appuser
CMD ["python", "/app/app.py"]
docker build -f Dockerfile.fixed -t trivy-lab:fixed .
trivy image --severity HIGH,CRITICAL trivy-lab:fixed

6. Gate a build on severity and generate an SBOM

# Non-zero exit code if HIGH/CRITICAL vulns exist -> perfect for CI
trivy image --exit-code 1 --severity HIGH,CRITICAL --ignore-unfixed trivy-lab:fixed
echo "Gate exit code: $?"

# CycloneDX SBOM
trivy image --format cyclonedx --output sbom.cdx.json trivy-lab:fixed
python3 -c "import json;d=json.load(open('sbom.cdx.json'));print(len(d['components']),'components')"

Expected output

$ trivy image --severity HIGH,CRITICAL python:3.4-alpine
python:3.4-alpine (alpine 3.9.2)
Total: 35 (HIGH: 27, CRITICAL: 8)
┌────────────┬────────────────┬──────────┬─────────────────┐
│  Library   │ Vulnerability  │ Severity │ Fixed Version   │
├────────────┼────────────────┼──────────┼─────────────────┤
│ musl       │ CVE-2019-14697 │ CRITICAL │ 1.1.20-r5       │
│ openssl    │ CVE-2019-1543  │ HIGH     │ 1.1.1b-r1       │
...

$ trivy image --scanners vuln,secret trivy-lab:vulnerable
...
trivy-lab:vulnerable (secrets)
CRITICAL: AWS (aws-secret-access-key)
  ENV AWS_SECRET_ACCESS_KEY=*****

$ trivy image --severity HIGH,CRITICAL trivy-lab:fixed
trivy-lab:fixed (alpine 3.21.x)
Total: 0 (HIGH: 0, CRITICAL: 0)
Gate exit code: 0

Troubleshooting

  • failed to download vulnerability DB: network/proxy issue or GitHub rate limiting. Retry, or set TRIVY_DB_REPOSITORY to a mirror; behind a proxy export HTTPS_PROXY.
  • Scan is very slow the first time: that is the one-time DB download. Subsequent scans use the local cache in ~/.cache/trivy.
  • image not found for a locally built image: Trivy defaults to the Docker daemon but can fall back to remote registries. Make sure the tag matches docker images output exactly and Docker is running.
  • Secret not detected: secrets baked via ENV are found in image config; ensure you scan the built image, not the Dockerfile, with --scanners secret.
  • trivy config . reports nothing: the Dockerfile must be in the scanned directory and named Dockerfile*. Check you are in trivy-lab/.

Cleanup

docker rmi trivy-lab:vulnerable trivy-lab:fixed python:3.4-alpine python:3.13-alpine
trivy clean --all
cd .. && rm -rf trivy-lab