How to Set Up Jenkins for Container Vulnerability Scanning with Trivy

How to Set Up Jenkins for Container Vulnerability Scanning with Trivy

Of all the security tools I’ve integrated into Jenkins pipelines, Trivy is by far the easiest to get started with, and it’s the one I recommend first to any team just beginning their container security journey. It’s open source, fast, requires no server setup, and runs as a single binary or container — no licensing negotiations, no complex API authentication flows. I added Trivy to a pipeline in an afternoon the first time I tried it, and it’s been part of every container-based project I’ve worked on since.

This guide covers everything from installing Trivy, running it inside Jenkins, scanning both images and infrastructure-as-code, and gating deployments based on findings.

Why Trivy for Container Security

Trivy, built by Aqua Security, is a comprehensive open-source scanner that detects:

Because it runs as a lightweight CLI tool, it fits naturally into a Jenkins pipeline step without needing a separate scanning server or complex API polling like some enterprise DAST tools require.

Jenkins Architecture Context

Trivy scans typically run right after the docker build step and before docker push, catching vulnerable base images or dependencies before they ever reach a registry. Because Trivy is just a CLI, Jenkins invokes it directly via sh steps — there’s no REST API polling loop needed, which makes this one of the simplest security integrations to build and maintain.

Prerequisites

Step 1: Install Trivy on the Jenkins Agent

On a Debian/Ubuntu-based Jenkins agent:

sudo apt-get install wget apt-transport-https gnupg lsb-release
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
echo "deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee -a /etc/apt/sources.list.d/trivy.list
sudo apt-get update
sudo apt-get install trivy

Alternatively, skip installation entirely and just run Trivy as a Docker container, which is often cleaner for CI environments:

docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy image myapp:latest

Step 2: Basic Jenkinsfile with Trivy Image Scanning

pipeline {
    agent any

    environment {
        IMAGE_NAME = 'myapp'
        IMAGE_TAG = "${env.BUILD_NUMBER}"
    }

    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }

        stage('Build Docker Image') {
            steps {
                sh "docker build -t ${IMAGE_NAME}:${IMAGE_TAG} ."
            }
        }

        stage('Trivy Vulnerability Scan') {
            steps {
                sh """
                    trivy image \
                        --severity HIGH,CRITICAL \
                        --exit-code 1 \
                        --format table \
                        --output trivy-report.txt \
                        ${IMAGE_NAME}:${IMAGE_TAG}
                """
            }
        }

        stage('Push Image') {
            steps {
                sh "docker push ${IMAGE_NAME}:${IMAGE_TAG}"
            }
        }
    }

    post {
        always {
            archiveArtifacts artifacts: 'trivy-report.txt', allowEmptyArchive: true
        }
        failure {
            mail to: 'devops-team@example.com',
                 subject: "Trivy Scan Failed: ${IMAGE_NAME}:${IMAGE_TAG}",
                 body: "High/Critical vulnerabilities detected. See attached report."
        }
    }
}

The --exit-code 1 flag is what makes Trivy actually fail the Jenkins build when it finds vulnerabilities at or above the specified severity — without it, Trivy just reports and exits 0 regardless of findings.

Step 3: JSON Output for Programmatic Gating

For more nuanced gating logic (say, allowing a small number of medium vulnerabilities but blocking on any critical), use JSON output and parse it with readJSON:

stage('Trivy Scan with Custom Gating') {
    steps {
        sh """
            trivy image --format json --output trivy-results.json ${IMAGE_NAME}:${IMAGE_TAG}
        """
        script {
            def results = readJSON file: 'trivy-results.json'
            def criticalCount = 0
            results.Results.each { result ->
                result.Vulnerabilities?.each { vuln ->
                    if (vuln.Severity == 'CRITICAL') {
                        criticalCount++
                    }
                }
            }
            if (criticalCount > 0) {
                error "Found ${criticalCount} CRITICAL vulnerabilities. Failing build."
            } else {
                echo "No critical vulnerabilities found."
            }
        }
    }
}

Step 4: Using the Trivy Jenkins Plugin

There’s also an official Trivy plugin for Jenkins that provides built-in report visualization:

stage('Trivy Scan') {
    steps {
        trivy(
            image: "${IMAGE_NAME}:${IMAGE_TAG}",
            severity: 'HIGH,CRITICAL',
            format: 'table'
        )
    }
}

(Note: plugin availability and exact syntax may vary by version — check the plugin page for the latest usage details.)

Scanning Dockerfiles and IaC for Misconfigurations

Trivy isn’t limited to scanning built images — it can also scan your Dockerfile and Kubernetes/Terraform manifests directly for misconfigurations before you even build:

stage('Trivy Config Scan') {
    steps {
        sh 'trivy config --severity HIGH,CRITICAL --exit-code 1 .'
    }
}

This catches issues like running containers as root, missing resource limits in Kubernetes manifests, or overly permissive Terraform security group rules — all before the image is even built.

Scanning for Exposed Secrets

Trivy also includes a secret-scanning mode, useful for catching accidentally committed API keys or credentials baked into image layers:

stage('Trivy Secret Scan') {
    steps {
        sh "trivy image --scanners secret ${IMAGE_NAME}:${IMAGE_TAG}"
    }
}

Integrating with Git and GitHub

Report scan status directly on pull requests:

post {
    failure {
        githubNotify status: 'FAILURE', context: 'trivy-scan', description: 'Critical vulnerabilities found'
    }
    success {
        githubNotify status: 'SUCCESS', context: 'trivy-scan', description: 'No critical vulnerabilities'
    }
}

Integrating with Kubernetes

For clusters already running workloads, Trivy Operator can continuously scan images running in your cluster, complementing the CI-time scan with runtime visibility:

apiVersion: aquasecurity.github.io/v1alpha1
kind: VulnerabilityReport

Jenkins-triggered scans catch issues before deployment; the Trivy Operator catches new CVEs discovered after deployment for images that haven’t changed but whose vulnerability databases have been updated.

Integrating with Maven and Application Dependencies

Beyond container images, Trivy can scan a filesystem directly for language-specific dependency vulnerabilities, useful for Java/Maven projects even before containerization:

stage('Trivy Filesystem Scan') {
    steps {
        sh 'trivy fs --severity HIGH,CRITICAL --exit-code 1 .'
    }
}

This catches vulnerable dependencies declared in pom.xml, package.json, requirements.txt, and similar manifest files.

Monitoring and Reporting

For teams wanting a persistent, searchable history of scan results beyond individual build artifacts, consider piping Trivy JSON output into a centralized dashboard like DefectDojo or Elastic/Kibana. This gives you trend analysis across builds — “are we introducing more vulnerabilities over time, or fewer?” — which is hard to answer from individual Jenkins console logs alone.

Troubleshooting Common Issues

Best Practices

FAQs

Is Trivy really free for commercial use? Yes, Trivy is fully open source under the Apache 2.0 license, with no licensing fees for commercial or enterprise use.

Can Trivy scan private registries? Yes, Trivy supports scanning images directly from private registries (ECR, GCR, Docker Hub private repos, Harbor) given proper registry authentication.

How often is the Trivy vulnerability database updated? Trivy’s vulnerability database updates multiple times daily, pulling from sources like NVD, GitHub Security Advisories, and various OS vendor security trackers.

Does Trivy replace the need for a DAST tool like Burp Suite or Acunetix? No — Trivy focuses on known vulnerabilities in packages/dependencies and misconfigurations (a form of SCA/IaC scanning), while DAST tools test running applications for exploitable behavior. They’re complementary, not interchangeable.

Summary

Trivy is one of the fastest ways to add meaningful, zero-cost container security scanning into a Jenkins pipeline. From scanning built images for known CVEs, to catching Dockerfile misconfigurations and exposed secrets, it slots cleanly into the build-before-push stage of any containerized CI/CD workflow. Combined with severity-based gating and artifact archiving, it gives your team continuous, low-friction visibility into the security posture of every image you ship.

References

Exit mobile version