Trivy is a surprisingly effective tool for scanning container images for vulnerabilities, and integrating it into GitHub Actions is remarkably straightforward.

Let’s see it in action. Imagine a simple GitHub Actions workflow that builds a Docker image and then scans it.

name: Build and Scan Image

on:
  push:
    branches:
      - main

jobs:
  build-and-scan:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout code
      uses: actions/checkout@v3

    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v2

    - name: Log in to Docker Hub
      uses: docker/login-action@v2
      with:

        username: ${{ secrets.DOCKERHUB_USERNAME }}


        password: ${{ secrets.DOCKERHUB_TOKEN }}


    - name: Build and push Docker image
      uses: docker/build-push-action@v4
      with:
        context: .
        push: true
        tags: your-dockerhub-username/your-image-name:latest

    - name: Scan Docker image with Trivy
      uses: aquasecurity/trivy-action@master
      with:
        image-ref: your-dockerhub-username/your-image-name:latest
        format: 'table'
        ignore-unfixed: true
        exit-code: '1' # Exit with 1 if vulnerabilities are found
        vuln-type: 'os,library'
        severity: 'CRITICAL,HIGH'

This workflow does a few things: checks out your code, sets up Docker, logs into your Docker registry, builds your image, pushes it, and then scans it. The trivy-action step is where the magic happens. It pulls the specified image-ref, scans it for OS and library vulnerabilities, and importantly, exit-code: '1' means the workflow will fail if any critical or high severity vulnerabilities are found.

The core problem Trivy solves is that building container images often pulls in a vast number of dependencies, and keeping track of known security flaws within those dependencies is a constant, manual, and often impossible task. Trivy automates this by comparing the package versions in your image against a regularly updated vulnerability database.

Internally, Trivy works by first inspecting the layers of your container image. It identifies the operating system and the installed packages (e.g., apt, yum, apk for OS packages, and npm, pip, gem for application dependencies). For each identified package and version, it queries its local or remote vulnerability database. This database is a curated collection of Common Vulnerabilities and Exposures (CVEs) from sources like the National Vulnerability Database (NVD), vendor advisories, and GitHub Security Advisories. Trivy then reports any matches, along with details like severity, affected versions, and links to more information.

The key levers you control are primarily within the trivy-action configuration. image-ref is straightforward: it’s the name of the image you want to scan. format can be table, json, or sarif for different output styles. ignore-unfixed is crucial for managing noise; if a vulnerability has no available fix (e.g., it’s in a package that’s no longer maintained), you can choose to ignore it. exit-code is your gatekeeper – setting it to 1 means the build pipeline will break if vulnerabilities are found. vuln-type lets you specify whether to scan os packages, library dependencies, secret files, or IaC (Infrastructure as Code) configurations. Finally, severity allows you to filter by UNKNOWN, LOW, MEDIUM, HIGH, and CRITICAL, so you can focus on the most impactful issues.

What many users don’t realize is that Trivy doesn’t just scan for CVEs; it can also detect misconfigurations in your container images and infrastructure-as-code files. This extends its utility beyond just package vulnerabilities to broader security posture checks.

The next logical step after scanning is to automate the remediation of identified vulnerabilities.

Want structured learning?

Take the full Github-actions course →