A fast, focused vulnerability scanner for container images and filesystems, built to pair with Syft for SBOM generation. You'll scan an image, generate an SBOM once and scan it many times, filter to fixable findings, gate CI with --fail-on, and manage exceptions with an ignore file. Hands-on throughout.
The journey:
| You need | Why |
|---|---|
The grype CLI |
Scan images and SBOMs |
The syft CLI (optional) |
Generate SBOMs to feed Grype |
| A terminal | Everything starts on the CLI |
Install both from Anchore (brew install grype syft, or the install script, or Docker images). Grype downloads a vulnerability database on first run and caches it. Confirm: grype version.
Grype does one job well: find known vulnerabilities in software. It accepts several source types:
grype <image> -> scan a container image (local or remote)
grype dir:. -> scan a filesystem path
grype sbom:./sbom.json -> scan a previously generated SBOM
The tight scope is the point — Grype is small, fast, and scriptable, and it composes cleanly with Syft (which produces the SBOM) so you separate "what's installed" from "what's vulnerable."
grype nginx:1.25
grype myregistry.example.com/app:latest
grype docker:myapp:local # explicitly use the Docker daemon source
Grype catalogs the OS packages and language dependencies in the image, matches them against its vulnerability database, and prints a table: package, installed version, the version that fixes it, the vulnerability id, and severity.
The fix state column is what drives action. A vulnerability is fixed (a patched version exists), not-fixed (no patch yet), wont-fix, or unknown:
# Only show findings that have a fix available
grype nginx:1.25 --only-fixed
# Output formats for tooling / dashboards
grype nginx:1.25 -o json > grype.json
grype nginx:1.25 -o sarif > grype.sarif
grype nginx:1.25 -o cyclonedx > grype.cdx.json
--only-fixed focuses you on vulnerabilities a developer can actually resolve by bumping a version. Everything else is tracked, not blocked — there is no action to take on an unpatched CVE beyond removing the component.
Syft inspects an artifact and emits an SBOM (a complete inventory). Grype then scans that SBOM — much faster than re-cataloging the image, and it lets you re-check a release against tomorrow's CVE data:
# 1. Generate an SBOM with Syft
syft nginx:1.25 -o cyclonedx-json > sbom.cdx.json
# 2. Scan the SBOM with Grype (no image access needed)
grype sbom:./sbom.cdx.json
# One-liner: pipe Syft straight into Grype
syft nginx:1.25 -o json | grype
Store the SBOM as a build artifact. When a new CVE drops next month, re-run grype sbom:... against the exact bits you shipped — no rebuild required. This is the backbone of continuous, artifact-accurate vulnerability tracking.
Grype exits non-zero when it finds something at or above a threshold, which is how you gate CI:
# Fail if any Critical (or higher) vulnerability is present
grype nginx:1.25 --fail-on critical
# Combine with only-fixed so you block only on actionable Criticals
grype nginx:1.25 --only-fixed --fail-on critical
--fail-on takes negligible|low|medium|high|critical. Pair it with --only-fixed so the pipeline blocks on fixable Criticals and doesn't wedge on a CVE nobody can patch.
When you must accept a specific finding, record it explicitly in .grype.yaml rather than lowering the whole threshold:
# .grype.yaml
ignore:
- vulnerability: CVE-2023-12345 # unfixed in base image; revisit 2026-Q4
- vulnerability: CVE-2022-99999
fix-state: not-fixed
fail-on-severity: critical
only-fixed: true
Every ignore should name a specific vulnerability and carry a reason and a review date in a comment. A pinned, documented exception is safe; blanket-lowering --fail-on hides everything.
# .github/workflows/grype.yml
name: grype
on: [pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- name: Scan with Grype
uses: anchore/scan-action@v4
with:
image: myapp:${{ github.sha }}
fail-build: true
severity-cutoff: critical
only-fixed: true
Or call the binary directly and cache ~/.cache/grype/db between runs so the vulnerability database isn't re-downloaded every build. Upload the SARIF output to your code-scanning dashboard so findings sit on the pull request.
Both scan images well; the differences are scope and composition:
Rule of thumb: Grype + Syft when you're building an SBOM-centric supply-chain workflow and want a sharp, single-purpose scanner; Trivy when you want breadth (image + IaC + secrets) from one command. Running both a scanner mostly multiplies triage unless one meaningfully catches what the other misses.
--fail-on critical --only-fixed so you block on fixable Criticals, not unpatchable noise..grype.yaml ignores with a reason and a revisit date — never a silent blanket threshold change.--only-fixed; there's no developer action on an unpatched vulnerability.--fail-on to pass the build. That hides everything; pin a specific ignore instead.grype nginx:1.25 and note the fix-state column.syft, then scan it with grype sbom:....--only-fixed and see the actionable subset.grype <img> --fail-on critical and check the exit code (echo $?)..grype.yaml with one pinned ignore entry and confirm it drops from the results.Self-check:
--only-fixed matter for gating?You now have the loop: image → fix state → Syft SBOM → scan the SBOM → --fail-on in CI → documented ignores. That's Grype as a sharp, SBOM-first vulnerability gate.