DEV Community

Ayi NEDJIMI
Ayi NEDJIMI

Posted on

Trivy vs Grype vs Snyk: Container Vulnerability Scanners Shootout

Every container image you ship is a potential attack surface. Running nginx with an unpatched OpenSSL? A base image with a known privilege-escalation CVE? Your scanner should catch it before your attacker does. The real question is which tool is worth integrating into your pipeline without drowning your team in noise.

I tested Trivy, Grype, and Snyk across real-world images — from a lean Alpine-based Go service to a bloated Python data science container. Here's what I found.

The Contenders at a Glance

Trivy (Aqua Security) is a general-purpose vulnerability scanner covering container images, filesystems, Git repositories, Kubernetes configs, and IaC. It's fast, fully open-source, and ships as a single binary with no daemon required.

Grype (Anchore) is narrower in focus — containers and filesystems. It's also open-source, uses the Syft SBOM engine under the hood for package enumeration, and integrates cleanly with the broader Anchore ecosystem.

Snyk takes a commercial-platform approach (with a free tier). It wraps vulnerability scanning in a developer-experience layer: fix suggestions, pull request checks, IDE plugins, and a web dashboard. Useful, but with trade-offs discussed below.

Installation and First Scan

All three are easy to run locally:

# Trivy — single binary, no auth needed
brew install aquasecurity/trivy/trivy
trivy image python:3.11-slim

# Grype — also single binary, no auth needed
brew install anchore/grype/grype
grype python:3.11-slim

# Snyk — requires an account and token
npm install -g snyk
snyk auth
snyk container test python:3.11-slim
Enter fullscreen mode Exit fullscreen mode

For CI/CD, Trivy and Grype both work without authentication. Snyk requires a token, adding friction in ephemeral build environments unless you already use Snyk's platform. Here's a minimal GitHub Actions step for Trivy that blocks the build on fixable HIGH or CRITICAL CVEs:

- name: Scan container image
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: ${{ env.IMAGE_TAG }}
    format: 'table'
    exit-code: '1'
    ignore-unfixed: true
    severity: 'CRITICAL,HIGH'
Enter fullscreen mode Exit fullscreen mode

Grype has an equivalent action (anchore/scan-action). Snyk offers a GitHub App that handles scanning at the repository level via a dashboard, but the CI step still requires an API token injected as a secret.

What Each Scanner Actually Finds

I scanned three images and compared raw finding counts:

  • python:3.11-slim — a common base image
  • node:18-alpine — leaner, Alpine-based
  • A custom Go API image built on golang:1.21-alpine

Trivy reported the most CVEs overall. On python:3.11-slim, it returned 78 vulnerabilities (14 HIGH, 2 CRITICAL on the test date) across OS packages. With --scanners vuln,secret, it also flags hardcoded credentials in image layers.

Grype found 71 on the same image. The delta stems from differences in how each tool maps package versions to CVE databases. Both pull from NVD, GitHub Advisory, and OS-specific sources (Debian Security Tracker, Alpine SecDB, Red Hat OVAL), but their normalisation logic diverges on edge cases.

Snyk found 65, but surfaced the most actionable output: a recommended base image (python:3.11.9-slim-bookworm) that would eliminate 42 findings in a single change. That kind of signal cuts real triage time.

For custom triage, parsing JSON output programmatically beats reading tables. Here's a Python snippet that reads Trivy's JSON output and prints only fixable HIGH/CRITICAL findings:

import json
import sys

def parse_trivy_report(path: str) -> None:
    with open(path) as f:
        report = json.load(f)

    for result in report.get("Results", []):
        target = result.get("Target", "")
        for vuln in result.get("Vulnerabilities", []):
            severity = vuln.get("Severity", "")
            fixed = vuln.get("FixedVersion", "")
            if severity in ("HIGH", "CRITICAL") and fixed:
                print(
                    f"[{severity}] {vuln['VulnerabilityID']} "
                    f"{target}: {vuln['PkgName']} {vuln['InstalledVersion']} "
                    f"-> fix available: {fixed}"
                )

if __name__ == "__main__":
    parse_trivy_report(sys.argv[1])
Enter fullscreen mode Exit fullscreen mode

Run it with:

trivy image --format json --output report.json my-image:latest
python parse_trivy.py report.json
Enter fullscreen mode Exit fullscreen mode

Grype and Snyk both produce structured JSON as well, though with different schemas. Trivy's is the most consistent for scripting.

Speed, DB Updates, and Offline Use

Trivy and Grype both download and cache their vulnerability databases locally. On first run, expect a 20-50 MB download. Subsequent runs use the cache and pull only diffs.

Approximate scan times on a 2024 M3 MacBook Pro:

Scanner Cold (no cache) Warm (cached)
Trivy ~35s ~8s
Grype ~40s ~10s
Snyk ~25s ~20s*

*Snyk sends image metadata to their servers for analysis on every scan — no true local cache. This has a privacy implication: proprietary package names, internal library versions, and image layer metadata all leave your environment. For regulated industries, that's a non-starter.

Trivy and Grype run entirely air-gapped after the DB download. This matters in finance, healthcare, and government contexts. If you're building a formal container security posture, tooling choice belongs in your threat model — our container security hardening checklists cover this decision point with specific control mappings.

Suppressing False Positives

Every scanner flags CVEs for package features you don't compile, vulnerabilities that require local shell access in an environment with no shell, or disputed findings. Managing that noise is where workflow matters.

Trivy uses a .trivyignore file at the repo root:

# HTTP/2 rapid reset — not exposed externally in this service
CVE-2023-44487
# Disputed, no practical impact in our configuration
CVE-2024-12345
Enter fullscreen mode Exit fullscreen mode

Grype uses .grype.yaml:

ignore:
  - vulnerability: CVE-2023-44487
    reason: "HTTP/2 rapid reset  not exposed in this service"
  - fix-state: not-fixed
Enter fullscreen mode Exit fullscreen mode

The fix-state: not-fixed rule is particularly useful: it silences all unfixed CVEs globally, letting you focus on what is actually actionable. Trivy's --ignore-unfixed flag does the same thing.

Snyk handles suppressions through its web UI, which is friendlier for non-security engineers but requires everyone to be in the Snyk platform to see the reasoning behind each ignored finding.

Which One Should You Use?

After running all three in CI for several months, here is my honest recommendation:

Use Trivy as your default. It covers containers, IaC, secrets, and Kubernetes manifests in one binary. The GitHub Actions integration is stable, the JSON output is scriptable, and there is no vendor dependency. This is the right choice for most teams.

Use Grype if SBOM generation is a compliance requirement. The Syft + Grype combination produces a reproducible, auditable artifact trail — a container's software bill of materials plus its known vulnerabilities, tied together. If your security team or customers are asking for SBOMs, this pipeline is the cleanest way to deliver them.

Use Snyk if your team has few security specialists and needs fix guidance embedded in developer tools. The "upgrade this base image to fix 40% of your findings" recommendation saves real time. The free tier works for open-source projects; serious production use pushes you toward a paid plan quickly.

In practice, many teams run Trivy in the pipeline (fast, free, CI-native) and offer Snyk IDE plugins to developers who want inline fix suggestions. That combination covers pipeline enforcement and developer experience without full vendor lock-in.

The Takeaway

Container scanning is table stakes now. The real problem is avoiding alert fatigue. Trivy is the best default for most teams: fast, open-source, no auth required, wide coverage. Grype wins on the SBOM compliance path. Snyk earns its seat when developer adoption and fix guidance matter more than control.

Whichever tool you integrate, block on CRITICAL findings with a fix available, suppress known-irrelevant CVEs explicitly, and review your ignore list quarterly. Noise without action is worse than no scanning at all.


I run AYI NEDJIMI Consultants, a cybersecurity consulting firm. We publish free security hardening checklists — PDF and Excel.

Top comments (0)