Trivy Docker Security: How to Scan and Fix Container Vulnerabilities

trivy docker image scanner

Shipping a Docker image without scanning it for vulnerabilities is like deploying code without running tests β€” you are leaving known problems undetected until they surface in production. Every Docker image you build is a stack of operating system packages, runtime libraries, and application dependencies. Any one of them could harbor a known CVE (Common Vulnerability and Exposure) that attackers actively exploit.

Trivy β€” developed by Aqua Security and now one of the most widely adopted open-source security scanners in the DevSecOps ecosystem β€” solves this problem. It scans Docker images, filesystems, Git repositories, Kubernetes clusters, and Infrastructure-as-Code configurations for vulnerabilities and misconfigurations, and reports findings with enough detail to act on them immediately.

This guide covers everything you need to know to start using Trivy for Docker security: installation, scanning Docker images, understanding scan output, integrating Trivy into CI/CD pipelines, and building a remediation strategy that keeps your container security posture strong over time.

Related reading: If you are building a broader container security strategy, this article pairs directly with our guide on Docker Container Security Best Practices, which covers hardening techniques that complement Trivy scanning.

Table of Contents

  1. What Is Trivy and Why Use It for Docker Security
  2. What Trivy Scans For
  3. Installing Trivy
  4. Scanning Docker Images with Trivy
  5. Understanding Trivy Scan Output
  6. Scanning Dockerfiles and Filesystem
  7. Filtering and Tuning Scan Results
  8. Integrating Trivy into CI/CD Pipelines
  9. Using Trivy with Docker Scout and Other Tools
  10. Trivy Configuration File
  11. Vulnerability Remediation Strategy
  12. Trivy in Kubernetes Environments
  13. Summary and Best Practices Checklist

1. What Is Trivy and Why Use It for Docker Security

Trivy (short for Trigger Vulnerability) is an open-source, all-in-one security scanner maintained by Aqua Security. Released in 2019, it has grown into the de facto standard for container image vulnerability scanning in the open-source DevSecOps toolchain β€” used by engineering teams ranging from startups to Fortune 500 companies.

Why Trivy stands out among container security scanners:

Zero configuration to start. Unlike some enterprise security tools that require lengthy setup, Trivy works out of the box with a single command. Point it at an image name and it downloads the vulnerability database, scans the image, and returns results β€” no server, no account, no API key required.

Comprehensive coverage. Trivy does not just scan OS packages. It covers OS-level packages (Alpine, Debian, Ubuntu, RHEL, Amazon Linux), language-specific packages (npm, pip, gem, Maven, Go modules, Cargo), misconfigurations in Dockerfiles and IaC files, exposed secrets in image layers, and SBOM (Software Bill of Materials) generation.

Multiple vulnerability databases. Trivy aggregates from NVD (National Vulnerability Database), GitHub Advisory Database, RedHat, Debian, Alpine, Ubuntu, and more β€” giving it broader coverage than scanners that rely on a single source.

CI/CD native. Trivy is designed for pipeline integration. It supports multiple output formats (JSON, SARIF, CycloneDX, table), configurable exit codes for pipeline gating, and has official GitHub Actions and GitLab CI integrations.

Actively maintained. With thousands of GitHub stars and regular releases, Trivy’s vulnerability database is updated daily. This matters because new CVEs are disclosed constantly.

2. What Trivy Scans For

Before running your first scan, it helps to understand exactly what Trivy detects and where.

Vulnerabilities (CVEs)

Trivy matches installed packages against known CVE records. For each finding, it reports:

  • CVE ID β€” the unique identifier for the vulnerability (e.g., CVE-2023-44487)
  • Severity β€” CRITICAL, HIGH, MEDIUM, LOW, or UNKNOWN
  • Package name and installed version
  • Fixed version β€” the version that patches the vulnerability (if available)
  • Description and references β€” links to NVD, vendor advisories, and exploit databases

Misconfigurations

Trivy checks Dockerfiles and other IaC files against security best practice rules β€” detecting issues such as:

  • Running as root user
  • Missing USER instruction
  • Using ADD instead of COPY for local files
  • Pinning FROM to :latest without a digest
  • Exposing sensitive ports
  • Missing HEALTHCHECK instruction

Exposed Secrets

Trivy scans image layers and repository files for accidentally committed secrets including AWS access keys, GitHub tokens, private keys, database connection strings, and API credentials.

License Compliance

Trivy can identify the licenses of all packages in an image β€” useful for organizations with compliance requirements around open-source license usage (GPL, MIT, Apache, etc.).

3. Installing Trivy

Trivy supports Linux, macOS, and Windows. Here are the most common installation methods.

Linux (Debian/Ubuntu)

bash
# Add Trivy's official apt repository
sudo apt-get install -y wget apt-transport-https gnupg lsb-release

wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | \
  gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null

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

sudo apt-get update
sudo apt-get install -y trivy

Linux (RHEL/CentOS/Amazon Linux)

# Add Trivy's yum repository
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 yum -y update sudo yum -y install trivy

macOS (Homebrew)

brew install aquasecurity/trivy/trivy

Docker (no installation required)

Run Trivy as a container β€” useful in environments where you cannot install software directly:

docker run --rm \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v $HOME/.cache:/root/.cache \
  aquasec/trivy:latest image nginx:1.25.3-alpine

Verify Installation

trivy --version
# Output: Version: 0.x.x

4. Scanning Docker Images with Trivy

The core use case: scanning a Docker image for vulnerabilities.

Scan a Public Image

trivy image nginx:1.25.3-alpine

Trivy downloads its vulnerability database on first run (stored locally in ~/.cache/trivy), then scans the image and prints a results table to the terminal.

Scan a Local Image

If you have built an image locally and it is not yet pushed to a registry:

# Build your image first
docker build -t myapp:latest .

# Scan the local image
trivy image myapp:latest

Scan an Image from a Private Registry

# For Docker Hub private repositories
docker login
trivy image myorg/private-app:v1.2.0

# For AWS ECR
aws ecr get-login-password --region ap-southeast-1 | \
  docker login --username AWS --password-stdin \
  123456789.dkr.ecr.ap-southeast-1.amazonaws.com

trivy image 123456789.dkr.ecr.ap-southeast-1.amazonaws.com/myapp:latest

Scan by Image Digest (for reproducibility)

Scanning by digest ensures you are always scanning the exact same image, regardless of tag reassignment:

trivy image nginx@sha256:a3a9f69e51c2bcf8a2b3a5c43e4d5cde6b3e4b3f2a1c6d8e9f0a1b2c3d4e5f6

Export Results to JSON

trivy image --format json --output trivy-results.json myapp:latest

JSON output is useful for parsing results programmatically or feeding into a security dashboard.

Here is a sample Trivy output and how to read it:

5. Understanding Trivy Scan Output

Here is a sample Trivy output and how to read it:

myapp:latest (debian 11.8)
==========================
Total: 47 (UNKNOWN: 0, LOW: 28, MEDIUM: 12, HIGH: 5, CRITICAL: 2)

LibraryVulnerabilitySeverityInstalled VersionFixed VersionDescription
libssl1.1CVE-2023-0286CRITICAL1.1.1n-0+deb11u41.1.1n-0+deb11u5OpenSSL X.400 address type confusion vulnerability.
libssl1.1CVE-2023-0215HIGH1.1.1n-0+deb11u41.1.1n-0+deb11u5OpenSSL use-after-free vulnerability during certificate verification.
libc-binCVE-2022-23219HIGH2.31-13+deb11u6Will not fixglibc stack buffer overflow vulnerability in sunrpc component.
curlCVE-2023-38545CRITICAL7.74.0-1.3+deb11u107.74.0-1.3+deb11u11SOCKS5 heap buffer overflow vulnerability in curl.

Reading the output:

Library β€” the package containing the vulnerability. In this case, libssl1.1 and curl are system-level packages.

Vulnerability β€” the CVE identifier. You can look up any CVE at https://nvd.nist.gov/vuln/detail/<CVE-ID> for full details.

Severity β€” prioritize CRITICAL and HIGH. MEDIUM and LOW are worth tracking but rarely require immediate action unless your threat model demands it.

Installed Version β€” what is currently in your image.

Fixed Version β€” the version that resolves the CVE. If this says “Will not fix”, the maintainer has assessed the CVE as not applicable or has no current patch planned. Use your judgment on whether to accept this risk.

Title β€” a short description of the vulnerability type. This helps you quickly assess exploitability without looking up the full CVE record.

Severity prioritization guide:

Severity LevelRecommended Action
CRITICALRemediate immediately and block deployment to production until the vulnerability has been resolved.
HIGHPrioritize remediation before the next release and include the fix in the current development sprint.
MEDIUMSchedule remediation during the next planned maintenance or patching cycle.
LOWMonitor and address during routine dependency updates and technical debt activities.
UNKNOWNPerform additional investigation, as the package may lack sufficient CVE database coverage or severity classification.

6. Scanning Dockerfiles and Filesystem

Beyond image scanning, Trivy also checks your Dockerfile for misconfigurations before you even build the image.

Scan a Dockerfile for Misconfigurations

trivy config ./Dockerfile

Example output for a poorly configured Dockerfile:

Dockerfile (dockerfile)
=======================
Tests: 24 (SUCCESSES: 19, FAILURES: 5, EXCEPTIONS: 0)
Failures: 5 (HIGH: 2, MEDIUM: 2, LOW: 1)

HIGH: Specify at least 1 USER command in Dockerfile with non-root user
  β†’ Add 'USER &lt;non-root-user>' to your Dockerfile

HIGH: Do not use 'latest' tag for base image
  β†’ FROM node:latest should be FROM node:20.10.0-alpine

MEDIUM: Add HEALTHCHECK instruction to your Dockerfile
  β†’ HEALTHCHECK CMD curl -f http://localhost/ || exit 1

Scan a Local Filesystem

Useful for scanning application source code before building an image:

# Scan current directory
trivy fs .

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

This scans lock files (package-lock.json, requirements.txt, Gemfile.lock, go.sum, pom.xml) for vulnerable dependencies β€” catching issues at the source code level before they enter the image.

Scan a Git Repository

# Scan a remote repository directly
trivy repo https://github.com/your-org/your-repo

Trivy clones the repository, scans dependencies and IaC files, and checks for exposed secrets β€” without requiring a local clone.

7. Filtering and Tuning Scan Results

Raw Trivy output on a base image can return dozens or hundreds of findings. Filtering helps you focus on what matters.

Filter by Severity

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

# Only show CRITICAL
trivy image --severity CRITICAL myapp:latest

Ignore Unfixed Vulnerabilities

Many LOW and MEDIUM findings have no available fix. Filter them out to reduce noise:

trivy image --ignore-unfixed myapp:latest

This is particularly useful for CI/CD pipeline gates β€” only fail the build on vulnerabilities that can actually be fixed right now.

Exit Code for Pipeline Gating

By default, Trivy exits with code 0 regardless of findings. For CI/CD pipeline integration, set a non-zero exit code when vulnerabilities above a threshold are found:

# Exit with code 1 if any HIGH or CRITICAL vulnerabilities are found
trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:latest

This causes the pipeline step to fail, blocking deployment of vulnerable images.

Using a .trivyignore File

Some vulnerabilities may be accepted risks β€” for example, a CVE that does not apply to your usage pattern or is formally accepted by your security team. Document these in a .trivyignore file in your project root:

# .trivyignore
# Accepted risk: CVE-2022-23219 - glibc sunrpc not used in this application
# Accepted by: security-team@company.com | Date: 2024-01-15 | Review: 2024-07-15
CVE-2022-23219

# Low severity, no fix available, not exploitable in our context
CVE-2021-33560

Always document why a CVE is ignored and set a review date. A .trivyignore entry without a justification is a security debt with no repayment plan.

8. Integrating Trivy into CI/CD Pipelines

Integrating Trivy into your CI/CD pipeline is the highest-impact step you can take β€” it makes vulnerability scanning automatic, consistent, and mandatory for every image that reaches production.

GitHub Actions

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

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

jobs:
  trivy-scan:
    name: Scan Docker Image
    runs-on: ubuntu-latest

    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: HIGH,CRITICAL
          ignore-unfixed: true
          exit-code: 1

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

This workflow builds your image on every push, scans it with Trivy, fails the pipeline if HIGH or CRITICAL vulnerabilities are found, and uploads results to GitHub’s Security tab for visibility.

GitLab CI

# .gitlab-ci.yml
trivy-scan:
  stage: security
  image: aquasec/trivy:latest
  variables:
    DOCKER_HOST: tcp://docker:2375
  services:
    - docker:dind
  script:
    - trivy image
        --exit-code 1
        --severity HIGH,CRITICAL
        --ignore-unfixed
        --format json
        --output trivy-report.json
        $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  artifacts:
    when: always
    reports:
      container_scanning: trivy-report.json
    paths:
      - trivy-report.json
    expire_in: 30 days
  allow_failure: false

Jenkins Pipeline

// Jenkinsfile
pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                sh 'docker build -t myapp:${BUILD_NUMBER} .'
            }
        }

        stage('Trivy Security Scan') {
            steps {
                sh '''
                    trivy image \
                      --exit-code 1 \
                      --severity HIGH,CRITICAL \
                      --ignore-unfixed \
                      --format json \
                      --output trivy-results-${BUILD_NUMBER}.json \
                      myapp:${BUILD_NUMBER}
                '''
            }
            post {
                always {
                    archiveArtifacts artifacts: 'trivy-results-*.json'
                }
            }
        }

        stage('Push to Registry') {
            when {
                expression { currentBuild.result == null }
            }
            steps {
                sh 'docker push myapp:${BUILD_NUMBER}'
            }
        }
    }
}

The Push to Registry stage only runs if the Trivy scan passed β€” ensuring no vulnerable image ever reaches your container registry.

9. Using Trivy with Docker Scout and Other Tools

Trivy is not the only tool in the container security ecosystem. Understanding how it compares and complements other tools helps you choose the right combination for your stack.

Trivy vs Docker Scout

Docker Scout is Docker’s built-in vulnerability scanner, integrated directly into the Docker CLI and Docker Hub. It uses the same underlying vulnerability databases as Trivy but is tightly coupled to the Docker ecosystem.

FeatureTrivyDocker Scout
InstallationStandalone command-line tool that can be installed independently.Integrated directly into the Docker CLI ecosystem.
CostCompletely free and open-source.Offers a free tier with additional paid subscription plans.
Scanning ScopeSupports container images, filesystems, Git repositories, Kubernetes clusters, and Infrastructure as Code (IaC).Focused primarily on container image analysis and recommendations.
CI/CD IntegrationExtensive support for GitHub Actions, GitLab CI/CD, Jenkins, Azure DevOps, and other automation platforms.Native integration with GitHub Actions and Docker workflows.
Output FormatsSupports Table, JSON, SARIF, CycloneDX, and other machine-readable formats.Provides output in Table and JSON formats.
Best Use CaseIdeal for DevSecOps teams requiring comprehensive, multi-target vulnerability scanning.Best suited for organizations heavily invested in Docker-native development workflows.

For most DevOps teams, Trivy is the more flexible choice. Docker Scout is a natural fit if your workflow is heavily Docker Hub-centric and you want scanning without additional tooling.

Trivy + Grype

Grype (by Anchore) is another popular open-source scanner with similar capabilities. Some teams run both Trivy and Grype in parallel to maximize CVE detection coverage, since their databases and detection logic differ slightly. This is particularly valuable for security-sensitive environments where false negatives are costly.

Trivy + Falco

Trivy handles static security (scanning images before deployment). Falco handles runtime security (detecting anomalous behavior in running containers). Together they provide defense in depth:

  • Trivy: “Does this image contain known vulnerabilities?”
  • Falco: “Is this running container doing something unexpected?”

This combination is covered in detail in our Docker Container Security Best Practices guide.

10. Trivy Configuration File

For teams with consistent scanning requirements, a Trivy configuration file (trivy.yaml) eliminates the need to repeat flags on every scan command.

# trivy.yaml β€” place in project root or ~/.trivy/trivy.yaml

# Global settings
quiet: false
debug: false
cache-dir: ~/.cache/trivy

# Image scan settings
image:
  input: ""

# Scan settings
scan:
  security-checks:
    - vuln
    - config
    - secret

# Vulnerability settings
vulnerability:
  type:
    - os
    - library
  ignore-unfixed: true
  severity:
    - HIGH
    - CRITICAL

# Output settings
format: table
output: ""

# Exit code when vulnerabilities found
exit-code: 1

# Ignore file
ignorefile: .trivyignore

With this configuration in place, running trivy image myapp:latest automatically applies all these settings without repeating flags every time.

11. Vulnerability Remediation Strategy

Finding vulnerabilities is only half the job. You need a systematic approach to fixing them β€” otherwise scan results accumulate without action.

Step 1: Triage by Severity and Exploitability

Not all CVEs are equal. CRITICAL severity does not automatically mean immediate exploitation risk β€” some CVEs have no public exploit, affect components you do not use, or require network access you have restricted. Triage based on:

  • Severity (CRITICAL/HIGH first)
  • Whether a fixed version is available (--ignore-unfixed filters these out)
  • Whether the vulnerable component is actually used by your application
  • Whether compensating controls exist (network isolation, WAF, minimal base image)

Step 2: Fix at the Source

Update the base image β€” the single highest-impact action. If your image is based on debian:11 and 30 vulnerabilities are in OS packages, switching to debian:12-slim or alpine:3.19 may eliminate most of them immediately.

# Before
FROM python:3.11-slim-bullseye

# After β€” newer base with fewer known CVEs
FROM python:3.12-slim-bookworm

Update application dependencies β€” for language-level vulnerabilities (npm, pip, Maven), update the affected packages in your dependency files and rebuild.

# Python β€” update vulnerable package
pip install --upgrade cryptography

# Node.js β€” update vulnerable package
npm update tough-cookie

# Rebuild the image after updating dependencies
docker build -t myapp:patched .

Step 3: Verify the Fix

Always re-scan after applying fixes to confirm the CVE is resolved:

# Scan before fix
trivy image --severity HIGH,CRITICAL myapp:before-fix

# Apply fix, rebuild image
docker build -t myapp:after-fix .

# Scan after fix β€” confirm vulnerability count decreased
trivy image --severity HIGH,CRITICAL myapp:after-fix

Step 4: Prevent Regression

  • Pin base image versions with digest: FROM python:3.12-slim@sha256:<digest> β€” ensures reproducible builds
  • Schedule automated rebuilds of base images weekly, even without application code changes
  • Set up Dependabot or Renovate to automatically open pull requests for vulnerable dependency updates
  • Gate your CI/CD pipeline on Trivy scan pass β€” no vulnerable image reaches production without a conscious decision to accept the risk

Step 5: Document Accepted Risks

For vulnerabilities you cannot fix immediately, document them formally:

  • Log accepted CVEs in .trivyignore with justification, accepting team, and review date
  • Track them in your security backlog
  • Schedule a review date β€” accepted risks that are never reviewed become permanent blind spots

12. Trivy in Kubernetes Environments

For teams running containerized workloads on Kubernetes, Trivy offers cluster-level scanning beyond individual image scanning.

Scan a Running Kubernetes Cluster

# Scan all running workloads in the cluster
trivy k8s --report summary cluster

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

# Scan a specific workload
trivy k8s deployment/myapp --namespace production

This scans the images used by all running pods, plus Kubernetes resource configurations (RBAC policies, Pod Security Admission settings, network policies) for misconfigurations.

Trivy Operator for Kubernetes

For continuous scanning in production clusters, the Trivy Operator runs as a Kubernetes controller that automatically scans new workloads as they are deployed and stores results as Kubernetes Custom Resources:

# Install Trivy Operator with Helm
helm repo add aquasecurity https://aquasecurity.github.io/helm-charts/
helm repo update
helm install trivy-operator aquasecurity/trivy-operator \
  --namespace trivy-system \
  --create-namespace \
  --set="trivy.ignoreUnfixed=true"

Once installed, query scan results with kubectl:

# List vulnerability reports for all workloads
kubectl get vulnerabilityreports --all-namespaces

# View detailed report for a specific workload
kubectl describe vulnerabilityreport myapp-deployment -n production

This gives you a continuous, automated security posture view across your entire cluster β€” not just point-in-time scans.

Summary and Best Practices Checklist

Trivy is one of the most practical additions you can make to your Docker security workflow. It is fast to install, requires no infrastructure, and immediately surfaces real security risks that would otherwise be invisible until exploitation.

Use this checklist as your Trivy implementation reference:

#PracticeStatus
1Install Trivy and scan all existing production images☐
2Add Trivy image scan to every CI/CD pipeline☐
3Set --exit-code 1 to gate deployments on CRITICAL/HIGH findings☐
4Use --ignore-unfixed to reduce noise from unpatchable CVEs☐
5Scan Dockerfiles with trivy config before building☐
6Scan application filesystem with trivy fs before building☐
7Use .trivyignore for formally accepted risks with justifications☐
8Schedule weekly base image rebuilds to pick up OS patches☐
9Use a trivy.yaml config file for consistent team-wide settings☐
10Upload SARIF results to GitHub Security tab for visibility☐
11Deploy Trivy Operator for continuous Kubernetes cluster scanning☐
12Re-scan after every fix to verify vulnerability resolution☐

Container security is not a destination β€” it is a continuous process. Trivy gives you the visibility to know where you stand, and the CI/CD integration to ensure that posture never silently degrades.

Continue your Docker security journey: This article is part of our container security series. For hardening techniques that complement Trivy scanning, read Docker Container Security Best Practices. For runtime threat detection in running containers, explore our upcoming guide on Falco for Kubernetes.

Using Trivy in your Docker or Kubernetes workflow? Have a .trivyignore strategy that works well for your team? Share your approach in the comments.

(Visited 4 times, 4 visits today)

You may also like