One open-source scanner for almost everything you need to check before shipping — container images, filesystems, Git repos, infrastructure-as-code misconfigurations, secrets, licenses, and SBOMs. You'll scan an image, read and filter findings, catch IaC and secret problems, generate an SBOM, and gate a CI pipeline on severity. Hands-on throughout.
The journey:
| You need | Why |
|---|---|
The trivy CLI |
Run every kind of scan locally |
| A terminal | Everything starts on the CLI |
| Docker (optional) | To build/scan a local image |
Install with brew install trivy, the apt/yum repo, or the official Docker image. The first scan downloads a vulnerability database (cached afterward). Confirm: trivy --version.
Trivy is a single binary that scans many targets for many problem classes. Instead of stitching together separate tools for image CVEs, IaC misconfig, and secrets, you learn one CLI:
trivy image <img> -> OS + language package CVEs in a container image
trivy filesystem <dir> -> dependencies + secrets + misconfig on disk
trivy repo <url> -> the same, against a Git repository
trivy config <dir> -> IaC misconfiguration (Terraform, k8s, Dockerfile)
trivy sbom <file> -> scan an existing SBOM for vulnerabilities
That breadth is why Trivy is a common default in pipelines — it covers the SCA and image-scanning stages, plus IaC and secrets, from one tool.
Point Trivy at any image reference — local or from a registry:
trivy image nginx:1.25
trivy image myregistry.example.com/app:latest
trivy image --scanners vuln python:3.12-slim
Trivy pulls the image metadata, identifies the OS and every language dependency, and matches them against known CVEs. The report groups findings by target (the OS layer, then each language manifest) with severity, installed version, and the version that fixes it.
Most of the noise in an image comes from the base OS, and much of it has no fix yet. Filter to what's actionable:
# Only show High and Critical
trivy image --severity HIGH,CRITICAL nginx:1.25
# Only show vulnerabilities that actually have a fix available
trivy image --ignore-unfixed --severity HIGH,CRITICAL nginx:1.25
# Machine-readable output
trivy image --format json --output report.json nginx:1.25
trivy image --format sarif --output report.sarif nginx:1.25
--ignore-unfixed is the single most useful flag day-to-day: it hides CVEs with no released patch, because there is no action a developer can take on them. Gate on fixable High/Critical; track the rest.
Trivy scans source trees and lockfiles directly, which is your SCA (software composition analysis) step:
trivy filesystem . # deps + secrets + misconfig in the working dir
trivy filesystem --scanners vuln . # just dependency CVEs
trivy repo https://github.com/org/project # scan a remote repo
It understands lockfiles across ecosystems (package-lock.json, go.sum, poetry.lock, Gemfile.lock, pom.xml, and more), so it finds vulnerable transitive dependencies without installing anything.
trivy config checks infrastructure-as-code against built-in policies for insecure settings — the shift-left check for your Terraform and manifests:
trivy config . # scan Terraform, k8s YAML, Dockerfiles, Helm
trivy config --severity HIGH,CRITICAL ./terraform
Typical findings: a security group open to 0.0.0.0/0, an S3 bucket without encryption, a container running as root or without resource limits, a Dockerfile using ADD from a URL. These are policy checks (like a built-in OPA/conftest) bundled into the same tool.
Trivy also looks for committed credentials — AWS keys, tokens, private keys — as part of filesystem and image scans:
trivy filesystem --scanners secret .
trivy image --scanners secret myapp:latest # catches secrets baked into layers
Scanning images for secrets is valuable because credentials often get copied into a layer and then "removed" in a later layer — but the earlier layer still contains them. Trivy sees every layer. For a dedicated secrets workflow with pre-commit hooks and history scanning, pair this with GitLeaks or TruffleHog.
A Software Bill of Materials lists everything in your artifact. Generate one, then scan it repeatedly without re-inspecting the image:
# Generate an SBOM in CycloneDX or SPDX format
trivy image --format cyclonedx --output sbom.cdx.json myapp:1.0
trivy image --format spdx-json --output sbom.spdx.json myapp:1.0
# Later, scan the SBOM for newly disclosed CVEs
trivy sbom sbom.cdx.json
This decouples "what's in the artifact" (fixed at build) from "what's vulnerable today" (changes as new CVEs are published), so you can re-check a shipped release against tomorrow's vulnerability data.
Make the build fail only on what you've agreed to block. --exit-code 1 turns findings into a non-zero exit:
# .github/workflows/trivy.yml
name: trivy
on: [pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Scan image
run: |
trivy image \
--severity HIGH,CRITICAL \
--ignore-unfixed \
--exit-code 1 \
myapp:${{ github.sha }}
Speed and quiet gating tips: cache the vuln DB between runs, use a .trivyignore file to accept specific CVE IDs with a comment, and set --exit-code 0 for informational scans that report without blocking.
# .trivyignore
CVE-2023-12345 # unfixed in base image, revisit when patched
--ignore-unfixed --severity HIGH,CRITICAL); track the rest as tickets, not blocks..trivyignore with a reason and a revisit date — never a permanent silent ignore.trivy config and --scanners secret are free coverage..trivyignore entries. Pin to a CVE id, add a reason and a date to revisit.trivy image nginx:1.25 and note how many findings come from the OS vs language packages.--ignore-unfixed --severity HIGH,CRITICAL and see the list shrink to the actionable set.trivy config on a Terraform or Kubernetes folder and fix one High misconfiguration.--exit-code 1 and watch it fail on a deliberately vulnerable image.Self-check:
--ignore-unfixed do, and why is it the most useful everyday flag?You now have the loop: image → filter fixable → filesystem/IaC/secrets → SBOM → CI exit code. That's Trivy as your one-stop pre-ship scanner.