Press ESC to close Press / to search

Trivy: Complete Container and Kubernetes Vulnerability Scanning Guide for DevSecOps

🎯 Key Takeaways

  • What Trivy Is and Why It Matters for DevSecOps
  • Installing Trivy on Linux
  • Scanning a Docker Image
  • Scanning a Dockerfile
  • Scanning a Filesystem

📑 Table of Contents

In the age of containerized applications and Kubernetes-orchestrated microservices, the attack surface for modern infrastructure has expanded dramatically. Container images bundle operating system libraries, language runtimes, and application dependencies — any of which might contain known vulnerabilities. Without automated scanning, these vulnerabilities can silently propagate from development to production. Trivy, developed by Aqua Security and released as open source, has become the de facto standard for vulnerability scanning in DevSecOps workflows. It is fast, comprehensive, easy to integrate, and completely free for the core scanning functionality.

What Trivy Is and Why It Matters for DevSecOps

Trivy is a comprehensive security scanner that detects vulnerabilities in container images, filesystems, Git repositories, Kubernetes clusters, and more. It checks for:

  • OS package vulnerabilities (CVEs in apt/yum packages)
  • Language package vulnerabilities (npm, pip, gem, cargo, go modules, Maven)
  • Infrastructure as Code misconfigurations (Terraform, Kubernetes YAML, Dockerfile)
  • Exposed secrets and credentials in code and configs
  • Software Bill of Materials (SBOM) generation
  • License compliance

Trivy uses multiple vulnerability databases including the National Vulnerability Database (NVD), GitHub Security Advisories, and language-specific advisory databases. It updates these databases automatically and caches them locally for fast repeated scans.

Installing Trivy on Linux

The easiest installation method on Debian/Ubuntu:

# Add the Trivy repository
sudo apt-get install -y wget apt-transport-https gnupg
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | 
  sudo gpg --dearmor -o /usr/share/keyrings/trivy.gpg

echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb generic main" | 
  sudo tee /etc/apt/sources.list.d/trivy.list

sudo apt-get update
sudo apt-get install trivy

# Verify installation
trivy --version

On RHEL, Fedora, Rocky Linux:

cat << EOF | sudo tee /etc/yum.repos.d/trivy.repo
[trivy]
name=Trivy repository
baseurl=https://aquasecurity.github.io/trivy-repo/rpm/releases/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://aquasecurity.github.io/trivy-repo/rpm/public.key
EOF

sudo dnf install trivy

Or via a single binary download:

# Download the latest release binary
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin

trivy --version

Scanning a Docker Image

The most common use case is scanning container images for vulnerabilities. Trivy can scan images from Docker Hub, private registries, or local images:

# Scan a public Docker image
trivy image nginx:latest

# Scan a specific version
trivy image python:3.11-slim

# Scan a locally built image
trivy image my-app:latest

# Only show CRITICAL and HIGH severity vulnerabilities
trivy image --severity CRITICAL,HIGH nginx:latest

# Output as JSON for machine processing
trivy image --format json --output results.json nginx:latest

# Output as a SARIF file (for GitHub Security tab integration)
trivy image --format sarif --output results.sarif nginx:latest

Example output (abbreviated):

2026-03-01T10:23:15Z INFO Vulnerability scanning is enabled
2026-03-01T10:23:18Z INFO Detected OS: debian 12.4
nginx:latest (debian 12.4)

Total: 8 (CRITICAL: 1, HIGH: 3, MEDIUM: 4)

┌──────────────┬────────────────┬──────────┬──────────────────────┬────────────────────────────┐
│   Library    │ Vulnerability  │ Severity │  Installed Version   │       Fixed Version        │
├──────────────┼────────────────┼──────────┼──────────────────────┼────────────────────────────┤
│ libssl3      │ CVE-2024-XXXXX │ CRITICAL │ 3.0.11-1             │ 3.0.15-1                   │
│ libcurl4     │ CVE-2024-YYYYY │ HIGH     │ 7.88.1-10            │ 7.88.1-10+deb12u7          │
└──────────────┴────────────────┴──────────┴──────────────────────┴────────────────────────────┘

Scanning a Dockerfile

Trivy can also scan Dockerfiles for configuration issues before even building the image:

# Scan a Dockerfile for misconfigurations
trivy config ./Dockerfile

# Scan an entire directory containing IaC files
trivy config ./infrastructure/

# Example of what Trivy flags in a Dockerfile:
# - Running as root (no USER directive)
# - Using ADD instead of COPY
# - Using latest tag
# - Not pinning package versions

Scanning a Filesystem

For scanning the filesystem of a running system or a checked-out repository:

# Scan the current directory
trivy fs .

# Scan a specific path
trivy fs /path/to/project

# Scan only for secrets (great for pre-commit hooks)
trivy fs --scanners secret .

# Scan for vulnerabilities in language dependencies only
trivy fs --scanners vuln .

Scanning a Kubernetes Cluster

One of Trivy’s most powerful features is its ability to scan an entire Kubernetes cluster. The trivy k8s command connects to your cluster using your current kubeconfig and scans all workloads:

# Scan the entire cluster (requires kubectl configured)
trivy k8s --report summary cluster

# Scan a specific namespace
trivy k8s --namespace production --report summary cluster

# Scan a specific resource
trivy k8s --report all deployment/nginx-deployment

# Get a detailed all-findings report
trivy k8s --report all cluster

# Output as JSON for SIEM integration
trivy k8s --format json --output k8s-scan.json --report all cluster

The Kubernetes scanner checks for:

  • Vulnerabilities in container images running in the cluster
  • Kubernetes resource misconfigurations (pods running as root, missing resource limits, privileged containers)
  • Exposed secrets in ConfigMaps and environment variables
  • RBAC misconfigurations

Scanning a Git Repository

# Scan a remote Git repository without cloning it
trivy repo https://github.com/your-org/your-repo

# Scan a local Git repository
trivy repo /path/to/local/repo

# Scan for secrets only in a repository
trivy repo --scanners secret https://github.com/your-org/your-repo

Understanding Severity Levels

Trivy uses the CVSS (Common Vulnerability Scoring System) to assign severity levels:

  • CRITICAL (CVSS 9.0-10.0): Must be fixed before deployment. Remote code execution, privilege escalation, or full system compromise.
  • HIGH (CVSS 7.0-8.9): Fix in the current sprint. Significant data exposure or system impact possible.
  • MEDIUM (CVSS 4.0-6.9): Fix in the next release cycle. Limited impact but should not be ignored.
  • LOW (CVSS 0.1-3.9): Fix when convenient. Minimal real-world impact.
  • UNKNOWN: The CVE has been reported but not yet scored.

Integrating Trivy into GitHub Actions

Adding Trivy to your CI/CD pipeline ensures vulnerabilities are caught before code reaches production. Here is a complete GitHub Actions workflow:

# .github/workflows/trivy-scan.yml
name: Trivy Security Scan

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]
  schedule:
    # Run daily at 2am UTC to catch newly published CVEs
    - cron: '0 2 * * *'

jobs:
  trivy-scan:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Build Docker image
        run: docker build -t my-app:${{ github.sha }} .

      - name: Run Trivy image scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: my-app:${{ github.sha }}
          format: sarif
          output: trivy-results.sarif
          severity: CRITICAL,HIGH
          exit-code: 1

      - name: Upload SARIF to GitHub Security tab
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: trivy-results.sarif

      - name: Run Trivy IaC scan
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: config
          scan-ref: .
          format: table
          exit-code: 1
          severity: CRITICAL,HIGH

Integrating Trivy into GitLab CI

# .gitlab-ci.yml
trivy-scan:
  stage: test
  image:
    name: aquasec/trivy:latest
    entrypoint: [""]
  variables:
    TRIVY_NO_PROGRESS: "true"
    TRIVY_CACHE_DIR: .trivycache
  cache:
    paths:
      - .trivycache/
  script:
    - trivy image
        --exit-code 1
        --severity CRITICAL
        --no-progress
        ${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHA}
  allow_failure: false
  artifacts:
    reports:
      sast: trivy-results.json

Managing False Positives with .trivyignore

Real-world scanning inevitably surfaces false positives — vulnerabilities in packages that are not actually exploitable in your context, or CVEs that have no available fix yet. The .trivyignore file lets you suppress specific findings:

# .trivyignore

# CVE with no fix available, accepted risk
CVE-2023-44487

# False positive - this package is not actually used at runtime
GHSA-wrvw-hg22-4m67

# Ignore all vulnerabilities in a specific package
golang.org/x/net

To use the ignore file:

trivy image --ignorefile .trivyignore my-app:latest

Generating an SBOM with Trivy

Software Bill of Materials (SBOM) is increasingly required for compliance and supply chain security. Trivy can generate SBOMs in CycloneDX or SPDX format:

# Generate an SBOM in CycloneDX format
trivy image --format cyclonedx --output sbom.cdx.json my-app:latest

# Generate an SBOM in SPDX format
trivy image --format spdx-json --output sbom.spdx.json my-app:latest

# Scan an existing SBOM for vulnerabilities
trivy sbom sbom.cdx.json

Trivy has earned its place as the essential vulnerability scanning tool for Linux-based DevSecOps workflows. Its combination of breadth (multiple scan targets), depth (comprehensive vulnerability databases), and ease of integration makes it the right choice whether you are a solo developer scanning personal projects or a security team enforcing compliance across a large Kubernetes fleet.

Trivy vs Other Container Security Scanners

Understanding where Trivy stands relative to its competitors helps you make an informed tool choice, or decide whether to use Trivy alongside other scanners for defense in depth.

Trivy vs Grype (Anchore)

Grype is the closest open-source competitor to Trivy. Both are fast CLI tools that scan container images and filesystems. Key differences:

  • Database: Trivy uses Aqua’s vulnerability database (aggregating NVD, GitHub, Red Hat, Ubuntu, etc.). Grype uses Anchore’s grype-db. Coverage is similar but not identical — running both occasionally finds different CVEs.
  • Scan targets: Trivy scans more target types (K8s clusters, IaC configs, secrets, SBOMs). Grype focuses specifically on software composition analysis.
  • Speed: Both are fast. Trivy has a slight edge on first-run database download; Grype caches more aggressively for subsequent runs.
  • Output: Trivy’s table output is more readable by default. Grype’s JSON output has a different schema that integrates well with the Anchore Enterprise platform.
# Install Grype for comparison
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin

# Scan same image with both tools
trivy image alpine:3.18 --severity HIGH,CRITICAL 2>/dev/null | grep "Total:"
grype alpine:3.18 --only-fixed 2>/dev/null | grep -c "^[A-Z]"

Trivy vs Snyk

Snyk is a commercial SaaS platform with a free tier. It offers developer-friendly integration with IDE plugins and pull request decoration. Compared to Trivy:

  • Snyk has better fix advice and prioritization (risk scoring considers exploitability, not just CVSS)
  • Trivy is fully offline-capable; Snyk requires internet connectivity for most features
  • Trivy is entirely free and open source; Snyk’s free tier has scan limits
  • Snyk’s language-level dependency scanning (especially for JavaScript/Python transitive deps) is more thorough

Trivy vs Clair

Clair (from CoreOS/Red Hat) is an older project that focuses specifically on container image layer scanning via an API server. It is commonly embedded in container registries (Harbor, Quay). Trivy is generally preferred for CLI/CI use due to simpler setup and broader scan scope.

Real-World DevSecOps Workflow with Trivy

A production DevSecOps pipeline uses Trivy at multiple stages: during development (scanning local builds), in CI (blocking on critical CVEs), in the registry (scanning on push), and in production (scheduled cluster scans).

# Complete multi-stage scanning script: scan-pipeline.sh
#!/bin/bash
set -euo pipefail

IMAGE="${1:-my-app:latest}"
SEVERITY="${2:-CRITICAL,HIGH}"
EXIT_CODE=0

echo "=== Stage 1: Base Image Scan ==="
BASE_IMAGE=$(docker inspect "$IMAGE" --format '{{.Config.Image}}' 2>/dev/null || echo "scratch")
if [ "$BASE_IMAGE" != "scratch" ] && [ -n "$BASE_IMAGE" ]; then
  trivy image --severity "$SEVERITY" --exit-code 1 "$BASE_IMAGE" || EXIT_CODE=1
fi

echo ""
echo "=== Stage 2: Application Image Scan ==="
trivy image   --severity "$SEVERITY"   --exit-code 1   --ignore-unfixed   "$IMAGE" || EXIT_CODE=1

echo ""
echo "=== Stage 3: IaC Configuration Scan ==="
trivy config   --severity "$SEVERITY"   --exit-code 1   . || EXIT_CODE=1

echo ""
echo "=== Stage 4: Secret Detection ==="
trivy fs   --scanners secret   --exit-code 1   . || EXIT_CODE=1

echo ""
echo "=== Stage 5: Generate SBOM ==="
trivy image   --format cyclonedx   --output "sbom-$(date +%Y%m%d).cdx.json"   "$IMAGE"

echo ""
if [ $EXIT_CODE -eq 0 ]; then
  echo "✅ All security checks passed"
else
  echo "❌ Security issues found - review output above"
fi

exit $EXIT_CODE

Trivy Best Practices

  1. Update the database daily in CI. New CVEs are published continuously. A stale database misses recent vulnerabilities. Add trivy image --download-db-only as a scheduled job.
  2. Use --ignore-unfixed in CI gates. Blocking on CVEs with no available fix is counterproductive — you cannot act on them. Gate on fixable vulnerabilities to keep pipelines actionable.
  3. Set severity thresholds deliberately. CRITICAL blocks merges. HIGH blocks releases. MEDIUM and LOW go to a tracking dashboard but do not block.
  4. Scan in the registry, not just in CI. New CVEs are discovered after images are built. Integrate Trivy into your container registry (Harbor has native Trivy integration) to scan on push and re-scan existing images on schedule.
  5. Generate and store SBOMs. An SBOM generated at build time lets you retroactively check which of your deployed images contain a newly-disclosed CVE without re-scanning.
  6. Treat .trivyignore as technical debt. Every entry needs a ticket with a remediation plan and deadline. Review the file quarterly and remove entries for CVEs that now have fixes available.

Was this article helpful?

Advertisement
🏷️ Tags: container security devsecops kubernetes security trivy vulnerability scanning
R

About Ramesh Sundararamaiah

Red Hat Certified Architect

Expert in Linux system administration, DevOps automation, and cloud infrastructure. Specializing in Red Hat Enterprise Linux, CentOS, Ubuntu, Docker, Ansible, and enterprise IT solutions.

🐧 Stay Updated with Linux Tips

Get the latest tutorials, news, and guides delivered to your inbox weekly.

Advertisement

Add Comment