Container Image Security Scanning with Trivy — Automating Vulnerability Detection in CI/CD Pipelines

Introduction

Container images are the building blocks of modern cloud-native applications, but they often ship with known vulnerabilities in base images, system packages, or application dependencies. A single unpatched CVE can expose your entire infrastructure to remote code execution, data breaches, or denial-of-service attacks.

In this tutorial, you’ll learn how to:

  • Install and configure Trivy — the most widely adopted open-source container image scanner
  • Scan Docker images for OS packages, library vulnerabilities, IaC misconfigurations, and secrets
  • Automate security scanning inside GitHub Actions CI/CD pipelines
  • Enforce security gates that block vulnerable images from reaching production

Prerequisites:

  • Docker Docker installed locally
  • A GitHub account with a repository
  • Basic familiarity with YAML and CI/CD concepts

What is Trivy and Why Container Scanning Matters

Trivy Trivy (short for “Trigger” — a vulnerability scanner) is an open-source security scanning tool created by Aqua Security. It has become the industry standard for container image scanning, operating system vulnerability detection, IaC misconfiguration checks, and secrets detection — all with zero configuration required.

Container images are composed of layers — base OS, system packages (apt/apk/yum), language-specific libraries (npm, pip, gem), and application code. Each layer can introduce vulnerabilities. Trivy cross-references package manifests against multiple vulnerability databases including:

  • NVD NVD (National Vulnerability Database)
  • Red Hat Red Hat CVE Database
  • Debian Debian / Ubuntu Security Advisories
  • Alpine Alpine SecDB
  • GHSA GitHub Advisory Database

Scanning should happen as early as possible in the development lifecycle — this is the “shift left” security principle. Catching a critical vulnerability in a PR prevents it from ever reaching a registry, a runtime environment, or — worst case — production.


Trivy Container Image Scanning Workflow

Developer Pushes Code git push / PR created

CI Builds Image docker build

Trivy Scans Image Vulnerability DB lookup

Critical or High Severity?

Yes

Pipeline FAILS Image blocked from registry

No

PASS

Push to Registry ECR / Docker Hub / GHCR

Action Decision/Scan Success Failure

The diagram above shows the complete Trivy scanning flow: code push triggers a CI build, the image is scanned, and depending on severity levels, the pipeline either passes (allowing the image to be pushed to a registry) or fails (blocking the vulnerable image).

Architecture Overview

When integrated into a CI/CD pipeline, Trivy sits between the image build step and the image push step. The architecture involves three key stages:


CI/CD Security Scanning Architecture

Source Control GitHub / GitLab / Bitbucket

CI Runner GitHub Actions / Jenkins

Container Registry ECR / Docker Hub / GHCR

Trivy Scanning Engine

Vulnerability DB CVE database (cached)

Scanner Engine OS + Library scanner

Report Generator JSON / SARIF / Table

GitHub Security Tab SARIF upload

Slack / Email Alerts Threshold-based alerts

Image Attestation Cosign + provenance

CI/CD Component Registry Output

This architecture diagram illustrates how Trivy plugs into your CI/CD pipeline: source code changes trigger a CI runner, which builds the image and passes it to the Trivy scanning engine. The engine cross-references the image layers against its cached vulnerability database and generates reports that can be pushed to GitHub Security, alerting systems, or image attestation tools.

Step 1: Setup — Install Trivy

Trivy can be installed on Linux, macOS, or Windows. It’s also available as a Docker image, which is the preferred method for CI/CD environments.

Option A: Local Installation (macOS / Linux)

# macOS — using Homebrew
brew install trivy

# Ubuntu / Debian
sudo apt-get install trivy

# RHEL / CentOS / Fedora
sudo dnf install trivy

Option B: Docker Image (Recommended for CI/CD)

# Pull the latest version
docker pull aquasec/trivy:latest

# Verify it works
docker run --rm aquasec/trivy:latest --version
i
Tip: Trivy caches the vulnerability database locally at ~/.cache/trivy. In CI/CD, use Docker volume mounts to persist the cache between runs for faster scans.

Step 2: Scan Container Images

Once installed, scanning a local Docker image is a single command. Let’s scan a Python image to see what vulnerabilities exist:

# Scan a local image
docker pull python:3.11-slim
trivy image python:3.11-slim

Trivy will output a color-coded table showing:

  • Library/package name and version
  • CVE ID (e.g., CVE-2024-XXXXX)
  • Severity (CRITICAL, HIGH, MEDIUM, LOW, UNKNOWN)
  • Fix version — the version that patches the vulnerability
  • Status — whether a fix is available

Selecting Severity Thresholds

For CI/CD enforcement, you typically want to fail the pipeline only on CRITICAL and HIGH severities:

# Only show critical and high severity
trivy image --severity CRITICAL,HIGH python:3.11-slim

# Exit with non-zero code if any vulnerabilities found
trivy image --severity CRITICAL,HIGH --exit-code 1 python:3.11-slim
!
Warning: Setting --exit-code 1 will cause the shell to report failure. In a pipeline, this stops execution. Use this intentionally — blocking on MEDIUM severity may be too aggressive for development environments.

Step 3: Scan Beyond Containers — IaC and Secrets

Trivy isn’t limited to container images. It can also scan Infrastructure-as-Code files, Kubernetes manifests, and detect hardcoded secrets.

# Scan IaC misconfigurations (Terraform, CloudFormation, etc.)
trivy config ./terraform/

# Scan Kubernetes manifests for security issues
trivy config ./k8s/deployment.yaml

# Scan filesystem for secrets (API keys, passwords)
trivy fs --scanners secret ./my-repo/

# Scan a remote Git repository
trivy repo https://github.com/org/repo.git

This makes Trivy a unified security scanner — one tool for images, configs, filesystems, and Git repos. No more cobbling together separate tools for each surface.

Step 4: Automate Scanning in GitHub Actions

This is where security becomes automated and enforceable. Here’s a complete GitHub Actions workflow that builds, scans, and conditionally pushes a Docker image:

name: Build and Security Scan

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build-and-scan:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write  # For SARIF upload

    steps:
      - name: Checkout code
        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: 'sarif'
          output: 'trivy-results.sarif'
          severity: 'CRITICAL,HIGH'
          exit-code: '1'

      - name: Upload Trivy results to GitHub Security
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: 'trivy-results.sarif'
          category: 'container-image-scan'

      - name: Push to registry (only if scan passed)
        if: success()
        run: |
          docker tag myapp:${{ github.sha }} ghcr.io/org/myapp:latest
          docker push ghcr.io/org/myapp:latest

Key Security Pattern: The exit-code: '1' parameter makes Trivy return a non-zero exit code when CRITICAL or HIGH vulnerabilities are found. This causes the Push to registry step to be skipped because it depends on if: success(). Vulnerable images never reach production.

Step 5: Verification — Test the Security Gate

Let’s verify the pipeline works as expected by creating a deliberately vulnerable Dockerfile and running Trivy against it:

# Create a vulnerable Dockerfile
cat > Dockerfile.vulnerable << 'EOF'
FROM python:3.7-slim  # Known EOL version with many CVEs
COPY app.py /app/
RUN pip install flask==1.0  # Old Flask with known vulns
CMD ["python", "/app/app.py"]
EOF

# Scan it — this should find CRITICAL/HIGH issues
trivy image --severity CRITICAL,HIGH --exit-code 1 \
  --ignore-unfixed=false \
  -f table docker.io/python:3.7-slim

If Trivy finds vulnerabilities, you’ll see:

  • A non-zero exit code (pipeline will fail)
  • A table of CVEs with severity, package, and fix version
  • The image is blocked from registry push

Now test with a secure image:

# Use the latest slim Python image
trivy image --severity CRITICAL,HIGH --exit-code 1 python:3.12-slim

# If no CRITICAL/HIGH vulns found, exit code is 0 (PASS)

Verifying in GitHub Security Tab

After the SARIF upload step runs, navigate to your GitHub repository and go to Security → Code scanning alerts. You’ll see all the Trivy-discovered vulnerabilities integrated into GitHub’s native security interface, with:

  • CVE descriptions and links
  • Severity classification
  • Affected package versions and fix suggestions
  • Alert history and dismissal capabilities

Conclusion

Container image security scanning with Trivy Trivy is one of the highest-impact security measures you can adopt for cloud-native workloads. In this tutorial, you learned:

  1. What Trivy is and how it cross-references packages against multiple vulnerability databases
  2. How to install Trivy locally or use it as a Docker image
  3. How to scan container images, IaC files, and detect secrets
  4. How to automate scanning in GitHub Actions with a security gate
  5. How to verify the pipeline blocks vulnerable images

By integrating Trivy into your CI/CD pipelines, you shift security left — catching vulnerabilities before they reach a registry or production environment. This is a core tenet of DevSecOps: security is not a gate at the end, but a continuous process throughout the delivery lifecycle.

Next steps:

  • Explore Trivy’s .trivyignore file to suppress false positives
  • Integrate with Slack Slack webhooks for real-time vulnerability alerts
  • Add Cosign Cosign image signing and verification alongside Trivy scanning
  • Enable scheduled scans of your container registry using GitHub Actions scheduled workflows

— Published by Nova Tech Cloud Tutorials

Leave a Comment

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

Scroll to Top