Container Image Scanning
Container image scanning is essential for detecting vulnerabilities before they reach production. This guide covers Trivy, Clair, Docker Scout, Grype, Snyk, and integrating vulnerability scanning into your CI/CD pipeline.
Containers are built from images that often contain hundreds of packages and libraries. Many of these packages have known vulnerabilities (CVEs) that attackers can exploit. Image scanning tools analyze container images and compare installed packages against vulnerability databases (NVD, Alpine SecDB, Debian Security Tracker, etc.).
Without scanning, you're shipping code with known, exploitable vulnerabilities. Image scanning should be integrated into your CI/CD pipeline to block vulnerable images before they reach production. Most container registries (Docker Hub, AWS ECR, Azure ACR, Google GCR) offer built-in or integrated scanning.
| Tool | Language | Database Updates | CI/CD Integration | Best For |
|---|---|---|---|---|
| Trivy | Go | Fast (auto-updates) | Excellent | All-round, fastest, easiest |
| Docker Scout | Built-in | Real-time | Excellent (GitHub Actions) | Docker users |
| Clair | Go | Manual/periodic | Good | Large-scale registries |
| Grype | Go | Fast | Good | Anchore ecosystem |
| Snyk | Node.js | Real-time | Excellent | Developer-first security |
| Dagda | Python | Good | Fair | Lightweight scanning |
Trivy is the most popular open-source container vulnerability scanner. It's fast, easy to use, and covers OS packages (Alpine, Debian, Ubuntu, CentOS, etc.) and language-specific packages (npm, pip, gem, cargo, go). Trivy also scans misconfigurations, secrets, and SBOMs.
# Install Trivy - macOS
brew install aquasecurity/trivy/trivy
# Install Trivy - Linux
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
# Scan image for vulnerabilities
trivy image nginx:latest
# Scan with severity filter
trivy image --severity CRITICAL,HIGH nginx:latest
# Scan with JSON output
trivy image --format json --output results.json nginx:latest
# Scan Dockerfile misconfigurations
trivy config ./Dockerfile
# Scan filesystem
trivy fs --security-checks vuln,config .
# Ignore unfixed vulnerabilities (for faster results)
trivy image --ignore-unfixed nginx:latest
Docker Scout is Docker's native vulnerability scanning tool. It's built into Docker Desktop and Docker Hub, and integrates with GitHub Actions. Docker Scout provides policy evaluation, base image upgrade recommendations, and SBOM generation.
# Docker Scout CLI (Docker Desktop)
docker scout quickview nginx:latest
docker scout cves nginx:latest
docker scout recommendations nginx:latest
# Compare two images
docker scout compare nginx:1.20 nginx:1.25
# Generate SBOM
docker scout sbom nginx:latest --format cyclonedx
# Policy evaluation (create policy file)
docker scout policy evaluate nginx:latest --policy policy.yml
# GitHub Actions integration
- name: Run Docker Scout
uses: docker/scout-action@v1
with:
command: cves
image: ${{ github.repository }}:latest
Clair is an open-source project from CoreOS (now Red Hat) for static analysis of container vulnerabilities. It's often used with registry integrations (Quay.io, Harbor) rather than as a CLI tool. Clair has a client-server architecture and is best for large-scale deployments.
# Run Clair with Docker Compose
version: '3.8'
services:
postgres:
image: postgres:15
environment:
POSTGRES_PASSWORD: clairpass
clair:
image: quay.io/projectquay/clair:latest
depends_on:
- postgres
ports:
- "8080:8080"
# Analyze image using clairctl
clairctl analyze -i nginx:latest
# Harbor integration (Harbor uses Clair for scanning)
# Built into Harbor registry
Grype is a powerful vulnerability scanner from the creators of Syft (SBOM tool). It's fast, accurate, and integrates well with the Anchore ecosystem. Grype can scan images, directories, and SBOM files.
# Install Grype - macOS
brew install anchore/grype/grype
# Install Grype - Linux
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
# Scan image
grype nginx:latest
# Scan with severity filter
grype nginx:latest --fail-on high
# Output in different formats
grype nginx:latest -o json
grype nginx:latest -o template -t custom.template
# Scan SBOM file (generated with Syft)
syft nginx:latest -o cyclonedx-json > sbom.json
grype sbom.json
Snyk offers container scanning with deep integration into developer workflows. It includes IDE plugins, GitHub integrations, and detailed remediation advice. Snyk's base image suggestions help you upgrade to secure base images.
# Install Snyk CLI
npm install -g snyk
snyk auth
# Test container image
snyk container test nginx:latest
# Test with severity threshold
snyk container test nginx:latest --severity-threshold=high
# Monitor image for ongoing alerts
snyk container monitor nginx:latest
# Get base image upgrade recommendation
snyk container test nginx:latest --file=Dockerfile
# GitHub Actions integration
- name: Run Snyk Container
uses: snyk/actions/docker@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
image: nginx:latest
args: --severity-threshold=high
Integrate scanning into your CI/CD pipeline to block vulnerable images from being deployed. Here's how to integrate Trivy with GitHub Actions:
# .github/workflows/container-scan.yml
name: Container Security Scan
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t myapp:latest .
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:latest'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
- name: Upload Trivy results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'
- name: Fail if critical vulnerabilities found
run: |
trivy image --exit-code 1 --severity CRITICAL myapp:latest
Most container registries have built-in or integrated vulnerability scanning:
# AWS ECR scanning
# Enabled in ECR settings (Basic or Enhanced scanning)
aws ecr describe-image-scan-findings --repository-name myapp --image-id imageTag=latest
# Azure ACR scanning
az acr scan --name myregistry --image myapp:latest
az acr check-health --name myregistry
# Google GCR / Artifact Registry scanning (uses On-Demand scanning)
gcloud artifacts docker images scan myrepo/myapp:latest
# Docker Hub scanning (uses Docker Scout)
# Automatic for official images and subscribed repositories
SBOMs (Software Bill of Materials) are machine-readable inventories of all components in a software artifact. Many regulations now require SBOMs. Tools like Syft, Trivy, and Docker Scout can generate SBOMs.
# Syft (SBOM generator)
syft nginx:latest -o json > sbom.json
syft nginx:latest -o cyclonedx-json > sbom.cyclonedx.json
syft nginx:latest -o spdx-json > sbom.spdx.json
# Trivy SBOM
trivy image --format cyclonedx --output sbom.json nginx:latest
# Docker Scout SBOM
docker scout sbom nginx:latest --format cyclonedx
# GitHub Actions SBOM upload
- name: Generate SBOM
run: syft nginx:latest -o cyclonedx-json > sbom.json
- name: Upload SBOM
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.json
Define policies to block images based on vulnerability severity, number of vulnerabilities, or specific CVEs. Use rego (Open Policy Agent) or simpler YAML-based policies.
# Trivy policy (YAML)
# policy.yaml
- name: "Block critical vulnerabilities"
description: "Images with critical CVEs are not allowed"
type: "Vulnerability"
condition: |
any(data.vulnerabilities, severity == "CRITICAL")
- name: "Block images with known exploits"
description: "CVEs with known exploits are blocked"
type: "Vulnerability"
condition: |
any(data.vulnerabilities, exploited == true)
# Run with policy
trivy image --ignore-policy policy.yaml nginx:latest
- Update base image - Use the latest patched version of your base image.
- Switch to minimal base image - Alpine or Distroless have smaller attack surfaces.
- Remove unnecessary packages - Use multi-stage builds to exclude build tools.
- Pin dependencies - Use version pinning to avoid unexpected updates.
- Apply security patches - Run `apt-get upgrade` or `apk upgrade` in Dockerfile.
- Use distroless images - Eliminates entire package categories (no shell, no package manager).
- Scan every image before deployment - Never skip scanning in CI/CD.
- Scan base images first - Scan the base images you use; they may have vulnerabilities.
- Set severity thresholds - Fail builds on CRITICAL and HIGH severity.
- Automate daily scans - Vulnerabilities are discovered daily; re-scan regularly.
- Generate SBOMs - For compliance and vulnerability tracking.
- Use policy as code - Enforce consistent security gates across teams.
- Integrate with registry scanning - Use built-in registry scanning for ongoing monitoring.
- Train developers - Ensure developers know how to interpret and fix vulnerabilities.
Container image scanning is a critical part of DevSecOps. Integrate scanning into your CI/CD pipeline to catch vulnerabilities before they reach production.