What is Trivy
Trivy is an open-source security scanner by Aqua Security. Unlike traditional vulnerability scanners that require a server-side database and complex setup, Trivy operates as a single binary with an embedded vulnerability database that auto-updates.
- Vulnerability Scanning — Detects known CVEs in OS packages (Alpine, Debian, RHEL) and language-specific packages (npm, pip, Go modules, Maven)
- Misconfiguration Detection — Checks Dockerfiles, Kubernetes manifests, Terraform, and CloudFormation for security issues
- Secret Detection — Finds hardcoded API keys, passwords, tokens, and certificates in code and container layers
- SBOM Generation — Creates Software Bill of Materials in CycloneDX or SPDX format
- License Scanning — Identifies package licenses for compliance checks
# Install Trivy on Linux/macOS
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.52.0
# Verify installation
trivy --version
# Version: 0.52.0
Image Scanning
Image scanning is Trivy's most common use case. It pulls the image (or uses a local copy), extracts layers, identifies packages, and checks each against the vulnerability database.
Basic Scan
# Scan a public image from Docker Hub
trivy image nginx:latest
# Scan a specific version
trivy image python:3.11-slim
# Scan a private registry image (uses Docker credentials)
trivy image ghcr.io/myorg/myapp:v1.2.3
# Scan a locally built image (no pull required)
docker build -t myapp:dev .
trivy image myapp:dev
The default output is a table showing vulnerability ID (CVE), package name, installed version, fixed version, and severity level.
Severity Filtering
# Show only HIGH and CRITICAL vulnerabilities
trivy image --severity HIGH,CRITICAL nginx:latest
# Show only CRITICAL (useful for CI gate decisions)
trivy image --severity CRITICAL alpine:3.18
# Ignore unfixed vulnerabilities (only show fixable ones)
trivy image --ignore-unfixed nginx:latest
# Combine: only fixable HIGH/CRITICAL
trivy image --severity HIGH,CRITICAL --ignore-unfixed python:3.11-slim
Output Formats
# Default: table format (human-readable)
trivy image --format table nginx:latest
# JSON output (for programmatic consumption)
trivy image --format json --output results.json nginx:latest
# SARIF format (for GitHub Security tab integration)
trivy image --format sarif --output trivy-results.sarif nginx:latest
# CycloneDX SBOM format
trivy image --format cyclonedx --output sbom.json nginx:latest
# Template-based output (custom formatting)
trivy image --format template \
--template "@contrib/html.tpl" \
--output report.html nginx:latest
CI Pipeline Integration
Trivy's true power emerges in CI/CD. By scanning every image before it's pushed to a registry or deployed, you create a security gate that blocks vulnerable images from reaching production.
GitHub Actions
# .github/workflows/security-scan.yaml
name: Container Security Scan
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
trivy-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t myapp:${{ github.sha }} .
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
format: table
exit-code: 1
severity: HIGH,CRITICAL
ignore-unfixed: true
- name: Upload SARIF to GitHub Security
if: always()
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
format: sarif
output: trivy-results.sarif
- name: Upload SARIF file
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-results.sarif
Exit Codes for CI Gates
# exit-code 0: always succeed (report only, don't block)
trivy image --exit-code 0 --severity HIGH,CRITICAL myapp:latest
# exit-code 1: fail the pipeline if vulnerabilities found
trivy image --exit-code 1 --severity CRITICAL myapp:latest
echo $? # 1 if CRITICAL CVEs found, 0 otherwise
# Common CI pattern: warn on HIGH, block on CRITICAL
trivy image --exit-code 0 --severity HIGH myapp:latest
trivy image --exit-code 1 --severity CRITICAL myapp:latest
--exit-code 1 --severity CRITICAL as a hard gate and --exit-code 0 --severity HIGH as an informational check. This blocks truly dangerous vulnerabilities while giving teams visibility into high-severity issues without disrupting deployments.
Filesystem & Config Scanning
Beyond images, Trivy scans source code directories for vulnerabilities in dependency lock files, and Infrastructure-as-Code files for misconfigurations.
# Scan the current directory for vulnerable dependencies
# (checks package-lock.json, requirements.txt, go.sum, pom.xml, etc.)
trivy fs .
# Scan only for misconfigurations in Kubernetes manifests
trivy config k8s/
# Scan a Dockerfile for misconfigurations
trivy config Dockerfile
# Scan Terraform files
trivy config --tf-vars terraform.tfvars terraform/
# Combined: vulnerabilities + misconfigs + secrets
trivy fs --scanners vuln,misconfig,secret .
Example Dockerfile misconfiguration findings:
# Trivy detects common Dockerfile issues:
# - Running as root (no USER instruction)
# - Using latest tag (non-reproducible builds)
# - ADD instead of COPY (unexpected behavior with URLs/archives)
# - Secrets in ENV or ARG instructions
trivy config Dockerfile
# OUTPUT:
# Dockerfile (dockerfile)
# ========================
# Tests: 23 (SUCCESSES: 20, FAILURES: 3)
# Failures: 3 (HIGH: 2, MEDIUM: 1)
#
# HIGH: Specify a tag in the 'FROM' statement
# ─────────────────────────────────────
# Dockerfile:1 - FROM python
#
# HIGH: Do not use 'root' user
# ─────────────────────────────────────
# No USER instruction found
Custom Policies
Trivy supports .trivyignore files to suppress known false positives and a trivy.yaml configuration file for persistent settings.
# .trivyignore — suppress specific CVEs
# Format: CVE-ID followed by optional comment
# Known false positive in our base image, no actual exposure
CVE-2023-44487
# Accepted risk: low-priority vuln in dev dependency only
CVE-2024-12345
# Temporarily ignored while waiting for upstream fix (expires 2026-07-01)
CVE-2026-98765 # TODO: remove after base image update
# trivy.yaml — persistent configuration
# Place in project root or ~/.trivy.yaml
scan:
# Security checks to perform
scanners:
- vuln
- misconfig
- secret
severity:
- HIGH
- CRITICAL
# Ignore unfixed vulnerabilities
ignore-unfixed: true
# Cache directory
cache:
dir: /tmp/trivy-cache
# Vulnerability database
db:
skip-update: false
# Output
format: table
# Image-specific settings
image:
# Remove credentials after scan
remove-pkgs: false
Exercises
nginx:latest, python:3.11-slim, alpine:3.18) with --severity HIGH,CRITICAL. Compare the number of vulnerabilities found in each. Which base image has the smallest attack surface?
FROM python:3.8) and verify the pipeline fails.
.trivyignore file to suppress a specific CVE from Exercise 1. Then create a trivy.yaml config file that sets default severity to HIGH,CRITICAL and enables misconfiguration scanning. Run trivy config on a Kubernetes deployment manifest and fix any misconfigurations Trivy reports.
Key Takeaways & Next Steps
- Trivy is a single-binary, zero-config scanner for vulnerabilities, misconfigurations, secrets, and SBOMs
trivy imagescans container images;trivy fsscans source code;trivy configscans IaC- Use
--severity HIGH,CRITICAL --exit-code 1in CI to block deployments with serious CVEs - SARIF output integrates with GitHub Security for centralized vulnerability tracking
.trivyignoresuppresses accepted risks;trivy.yamlstores persistent configuration- Filesystem scanning catches vulnerable dependencies before they're baked into images
Next in the Series
In Part 2: Operator & Cluster Reports, we'll deploy the Trivy Operator for continuous in-cluster scanning, explore VulnerabilityReport and ConfigAuditReport CRDs, and set up Prometheus/Grafana alerting on vulnerability findings.