catching vulnerabilities before they reach deployment
Image scanning checks every layer of a Docker image against databases of known vulnerabilities before a container even starts. Trivy and Grype are the two most widely used open source scanners for this, but they differ enough in database sources, speed, and extra features like SBOM generation and IaC checking to make the choice worth thinking through.
Table of Contents
- 1. Why image scanning is a mandatory step of its own
- 2. Trivy: architecture, data sources, and a first scan
- 3. Grype: architecture, data sources, and a first scan
- 4. Reading scan results and classifying severity
- 5. Defining severity gates in the CI pipeline
- 6. SBOM generation and traceability
- 7. Handling false positives and ignore lists
- 8. Choosing base images deliberately to reduce scan noise
- 9. Trivy and Grype head to head
- 10. Summary
- 11. FAQ
1. Why image scanning is a mandatory step of its own
Image scanning checks every layer of a Docker image against databases of known vulnerabilities before the image ever runs in production. A base image like `python:3.12-slim` brings system libraries along with the actual runtime environment, and those libraries can carry their own CVEs regardless of the application code. Without image scanning, these vulnerabilities stay invisible until a security incident or an external pentest uncovers them.
The decisive advantage of image scanning over pure code review is that it captures transitive dependencies that no developer tracks manually: system packages, language package manager dependencies like Composer or npm, and in some cases even license information. Trivy and Grype are the two most commonly used open source solutions for image scanning, both free, both CI ready, but with different strengths that are compared in detail below.
2. Trivy: architecture, data sources, and a first scan
Trivy from Aqua Security is an all in one scanner for image scanning that, besides container images, can also check file systems, Git repositories, Kubernetes manifests, and infrastructure as code for misconfigurations. Trivy's CVE database is aggregated from multiple sources, including NVD, GitHub Security Advisories, and distribution specific advisories such as Debian and Alpine security trackers, which results in a high hit rate for current distributions.
The first scan of an image with Trivy runs without prior configuration and automatically downloads the current vulnerability database on first invocation. In CI pipelines this database should be cached to avoid repeated downloads on every build, which shortens scan time considerably.
#!/usr/bin/env bash
# First Trivy scan against a built image, with severity filtering
set -euo pipefail
# Scan for OS packages and language dependencies, only report HIGH/CRITICAL
trivy image \
--severity HIGH,CRITICAL \
--ignore-unfixed \
--format table \
myregistry.example.com/shop-api:1.4.2
# Cache the vulnerability database between CI runs
trivy image --cache-dir /opt/trivy-cache --download-db-only
3. Grype: architecture, data sources, and a first scan
Grype from Anchore is more strongly focused on pure image scanning and file system analysis and is often used together with Syft, which generates the software bill of materials for an image that Grype then checks against its own vulnerability database. This separation between SBOM generation and vulnerability checking is an architectural difference from Trivy, one that allows an already generated SBOM to be checked against multiple database states without re-analyzing the image.
Grype uses its own aggregated database that pulls together similar sources as Trivy, but in practice sometimes returns different results, because the update cycles and normalization logic of the two projects operate independently. Whoever runs both scanners in parallel against the same image regularly finds CVEs that only one of the two tools reports.
#!/usr/bin/env bash
# Generate an SBOM with Syft, then scan it with Grype
set -euo pipefail
syft myregistry.example.com/shop-api:1.4.2 -o cyclonedx-json > sbom.json
grype sbom:sbom.json \
--fail-on high \
--only-fixed \
-o table
4. Reading scan results and classifying severity
Both Trivy and Grype classify findings into the levels `LOW`, `MEDIUM`, `HIGH`, and `CRITICAL`, based on the CVSS score of the respective vulnerability. This classification is a sensible starting point for image scanning, but not a substitute for contextual assessment: a `CRITICAL` vulnerability in a library that is never actually invoked inside your own container has a lower real priority in practice than a `HIGH` vulnerability in a library with direct network exposure.
Both tools support a filter for vulnerabilities that already have an available fix, `--ignore-unfixed` in Trivy and `--only-fixed` in Grype. This filter is especially valuable for image scanning in CI pipelines because it prevents a build from being blocked over a vulnerability for which the maintainer of the affected library has not yet published a patch. These findings land on a watch list instead, to be re-checked regularly.
In addition to the raw CVSS score, both tools often also report the EPSS value, which estimates the actual likelihood of a vulnerability being exploited in the wild. For image scanning in large environments with hundreds of findings per day, this value is often more informative than severity alone, because it distinguishes between theoretically critical and practically exploited vulnerabilities, noticeably easing prioritization for the team.
5. Defining severity gates in the CI pipeline
A build that fails on every `LOW` finding gets ignored quickly, because developers start routinely skipping the failed pipeline step. The practical approach for image scanning in CI is a tiered gate: `CRITICAL` findings block the merge immediately, `HIGH` findings trigger a mandatory review requirement, and `MEDIUM`/`LOW` findings land in a dashboard for regular triage without stopping the build.
Both scanners return via exit code whether findings above the configured threshold exist, which simplifies integration into any CI platform. It matters not to set this threshold statically the same for every project: an internally used admin tool without internet access can tolerate a looser gate than a publicly reachable payment interface.
#!/usr/bin/env bash
# CI gate: fail the pipeline only on CRITICAL findings with an available fix
set -euo pipefail
trivy image --severity CRITICAL --ignore-unfixed --exit-code 1 \
--format json --output trivy-report.json \
myregistry.example.com/payment-api:2.1.0
echo "[OK] No unfixed CRITICAL vulnerabilities found"
# Log HIGH findings separately for a review queue, without failing the build
trivy image --severity HIGH --format json --output trivy-high-report.json \
myregistry.example.com/payment-api:2.1.0 || true
6. SBOM generation and traceability
A software bill of materials lists every component of an image, including version number and license, regardless of whether a vulnerability is currently known. For image scanning, the SBOM is the starting point: if a new CVE is published tomorrow for a specific library version, an archived SBOM lets you immediately determine which production images are affected, without rescanning every image.
Trivy generates SBOMs directly with the `--format cyclonedx` or `--format spdx-json` flag, Grype typically uses Syft as a preceding step for that. Both formats, CycloneDX and SPDX, are readable by the majority of downstream tools, so the format choice usually depends on existing compliance requirements of the organization, not on the scanner itself.
#!/usr/bin/env bash
# Archive an SBOM per release, then re-check it against a fresh vulnerability database
set -euo pipefail
RELEASE_TAG="1.4.2"
syft "myregistry.example.com/shop-api:${RELEASE_TAG}" -o cyclonedx-json \
> "sboms/shop-api-${RELEASE_TAG}.cdx.json"
# Weeks later, re-scan the archived SBOM without touching the image again
grype "sbom:sboms/shop-api-${RELEASE_TAG}.cdx.json" --only-fixed -o table
7. Handling false positives and ignore lists
No image scanning tool is free of misclassifications. A library might be flagged as vulnerable even though the affected code path was disabled in your own image via a compile time option, or a CVE was meanwhile withdrawn as "not applicable" but the local database has not yet updated. For such cases, both scanners support project specific ignore files, `.trivyignore` in Trivy and a `.grype.yaml` with an ignore section in Grype.
Every entry in an ignore file should carry a short justification and an expiration date, so the list does not become a silent dumping ground for inconvenient findings. Without an expiration date, outdated exceptions often persist for years, even after the underlying assessment has long since changed.
It is also worthwhile to validate the ignore list regularly and automatically against the current database instead of maintaining it only once. A simple scheduled job that checks whether an ignored CVE has meanwhile received a fix prevents image scanning from silently missing a now fixable vulnerability forever, just because it was once marked unfixed months ago.
8. Choosing base images deliberately to reduce scan noise
The choice of base image has a direct impact on the result of image scanning, often more than any subsequent configuration of the scanner itself. A full `debian:bookworm` image brings along considerably more system packages than a `debian:bookworm-slim` or even a distroless image from Google, and every additional package is a potential source of new CVEs. Slim base images reduce the attack surface and, at the same time, the volume of findings that need to be assessed in the first place.
For languages with statically compiled binaries, such as Go, switching to a distroless or scratch image is worthwhile, ideally containing no system packages at all and therefore generating almost no OS level findings. For PHP or Node based applications, a fully package free image is rarely practical, but a deliberately minimal base image still noticeably reduces the hit rate of image scanning.
# Dockerfile snippet: comparing a full base image against a slim variant
# BEFORE: full Debian image, many system packages, larger scan surface
# FROM debian:bookworm
# AFTER: slim variant, fewer packages, noticeably fewer image scanning findings
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
9. Trivy and Grype head to head
Both scanners serve the same core purpose but differ in architecture, extra features, and integration depth. The following table contrasts the key differences.
| Criterion | Trivy | Grype | Relevance |
|---|---|---|---|
| Extra features | Integrated IaC, secret, and license scanning | Focused on vulnerabilities, SBOM via Syft | Trivy for all in one, Grype for modular pipelines |
| SBOM workflow | Directly integrated (--format cyclonedx) | Separate via Syft, reusable multiple times | Grype has an edge for frequent rescans without image access |
| Database sources | NVD, GHSA, distribution specific | Anchore's own aggregated database | Running both in parallel increases coverage |
| CI integration | Exit code, JSON, SARIF | Exit code, JSON, template output | Both equally CI ready |
In practice, combining both scanners is not redundant, it measurably increases the detection rate, because the database sources only partially overlap. Whoever has budget for only one tool should prefer Trivy for its broader extra features like IaC scanning; whoever already works with SBOM based compliance workflows benefits from the clean separation between Syft and Grype.
Mironsoft
Image scanning and severity gates for your CI pipelines
Do known vulnerabilities reach your deployments?
We integrate Trivy and Grype into your pipeline, define sensible severity gates instead of blanket blockages, and set up SBOM based traceability for new CVEs.
Scanner setup
Integrate Trivy and Grype into existing build pipelines
Severity gates
Define tiered thresholds instead of all or nothing blockages
SBOM tracking
Build traceability for new CVEs in already deployed images
10. Summary
Image scanning with Trivy or Grype closes a gap that pure code review can never cover: transitive dependencies and system packages that carry their own CVEs regardless of the application code. Both tools are free, CI ready, and support tiered severity filters, but they differ enough in extra features and database sources that combining both scanners regularly delivers additional findings.
What matters most for the practical success of image scanning is not the chosen tool alone, but a well thought out severity gate, a maintained ignore list with expiration dates, and the deliberate choice of slim base images that generate less attack surface and less scan noise from the start.
Image Scanning with Trivy and Grype — The essentials at a glance
Trivy
All in one scanner with IaC, secret, and license checks, broad database sources.
Grype
Focused on vulnerabilities, works with Syft SBOMs for repeatable checks.
Severity gates
CRITICAL blocks, HIGH forces review, MEDIUM/LOW into a dashboard instead of stopping the build.
Base images
Slim or distroless images reduce findings more than any scanner configuration.