Container Image Scanning with Trivy: Catch Vulnerabilities Before They Ship

Container images are your application’s most deployable artifact — and potentially its biggest attack surface. Every base image you inherit, every dependency you install, every layer you cache carries potential vulnerabilities. Trivy is a fast, comprehensive scanner that catches these issues before they reach production. Let’s set it up properly.

Why Trivy Over Alternatives

Several container scanners exist — Clair, Grype, Snyk — but Trivy stands out for three reasons: it’s a single binary with zero dependencies, it scans both OS packages and language-specific dependencies (Go, Node.js, Python, etc.), and it’s fast enough to run on every build. The Aqua Security team maintains it with regular vulnerability database updates.

Installing Trivy

# macOS
brew install trivy

# Linux (Debian/Ubuntu)
sudo apt-get install trivy

# Or grab the binary directly
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh

# Verify
trivy --version

Scanning Your First Image

# Basic scan — finds known CVEs
trivy image my-app:latest

# Filter to only HIGH and CRITICAL
trivy image --severity HIGH,CRITICAL my-app:latest

# Output as JSON for CI integration
trivy image --format json --output scan-results.json my-app:latest

The output shows each vulnerability with its CVE ID, affected package, installed version, and the version that fixes it. Trivy draws from multiple databases: NVD, GHSA, and distro-specific advisories like Debian’s DSA.

Integrating Into CI/CD

The real value comes from running Trivy automatically on every push. Here’s a GitHub Actions setup that fails builds on critical vulnerabilities:

# .github/workflows/security-scan.yml
name: Container Security Scan

on:
  push:
    branches: [main]
  pull_request:

jobs:
  trivy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

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

      - name: Run Trivy
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: app:${{ github.sha }}
          format: table
          exit-code: 1
          ignore-unfixed: true
          severity: CRITICAL,HIGH
          vuln-type: os,library

The exit-code: 1 flag makes the step fail when vulnerabilities are found, blocking the PR merge. The ignore-unfixed: true flag filters out vulnerabilities that don’t have a known fix — actionable findings only.

Scanning Infrastructure as Code

Trivy also scans Dockerfiles, Kubernetes manifests, and Terraform configurations for misconfigurations — not just vulnerabilities:

# Scan a Dockerfile for best practices
trivy config Dockerfile

# Scan Kubernetes manifests
trivy config ./k8s/

# Scan Terraform
trivy config ./terraform/

This catches issues like running as root, not setting resource limits, exposing sensitive ports, or using deprecated Kubernetes APIs. It’s like having a security-focused linter for your infrastructure.

Building a .trivyignore Responsibly

Some vulnerabilities can’t be fixed immediately — maybe the patched version breaks a dependency, or the vulnerable code path isn’t reachable. Trivy supports an ignore file:

# .trivyignore
CVE-2024-12345  # Pending upstream fix, expected in v2.3
CVE-2024-67890  # False positive — vulnerable code not linked

Every entry should have a comment explaining why it’s ignored and when it can be removed. Review this file monthly — stale ignores accumulate technical debt silently.

SBOM Generation

Software Bills of Materials are becoming mandatory for supply chain compliance. Trivy generates SBOMs in standard formats:

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

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

Store these artifacts alongside your releases. If a new CVE is disclosed, you can immediately check whether your deployed images contain the affected package without re-scanning.

Putting It All Together

A robust container scanning pipeline combines image scans, config scans, and SBOM generation into your build process. The setup cost is minimal — Trivy is free, fast, and integrates with existing CI platforms. The payoff is catching vulnerabilities before they become incidents, and having the documentation ready when auditors ask.

Start with a simple trivy image scan on your most-deployed image. You might be surprised by what’s hiding in those base layers.

Leave a Reply

Your email address will not be published. Required fields are marked *