Cybersecurity · Container Security
Container Security Scanning in 2026: What Trivy and Snyk Find That Your Pipeline Misses
Vulnerability scanning catches known CVEs in your base images and dependencies before they reach production. Here's how to set up Trivy and Snyk, understand their output, and act on what they find.
Anurag Verma
7 min read
Sponsored
Most teams scan their application code for security issues. Fewer scan their container images before pushing to production. The gap matters: base images accumulate CVEs as OS packages age, and a node:20 image pinned six months ago may carry vulnerabilities that have been fixed in the current tag.
Container image scanning doesn’t prevent all security problems (nothing does), but it catches a specific category of known-vulnerability exposure that’s entirely preventable. The tooling is straightforward, the integration is a few lines in a CI pipeline, and the alternative is running containers with vulnerabilities you don’t know about.
What Scanning Actually Does
A container image scanner builds a Software Bill of Materials (SBOM) for your image, which lists every package, library, and binary in every layer, and cross-references it against vulnerability databases (NVD, GitHub Advisory Database, OS vendor advisories). Matches produce CVE findings with severity scores.
Scanners find:
- OS-level CVEs: Vulnerabilities in Alpine, Debian, or Ubuntu packages in the base image
- Language package CVEs: npm packages with known vulnerabilities, pip packages, Maven dependencies
- Secrets in layers: Credentials, API keys, or private keys accidentally baked into image layers
- Misconfiguration risks: Running as root, exposed ports, capabilities
They don’t find:
- Logic bugs in your application code
- Runtime attack vectors that don’t involve known-vulnerability packages
- Zero-day vulnerabilities (the databases lag discovery by definition)
Trivy
Trivy is an open-source scanner from Aqua Security. It’s fast, accurate, and covers images, filesystems, Git repos, and Kubernetes clusters from the same tool.
Local Scanning
# Install
brew install trivy # macOS
# or via script:
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
# Scan a local image
trivy image myapp:latest
# Scan only HIGH and CRITICAL severities
trivy image --severity HIGH,CRITICAL myapp:latest
# Exit with non-zero code if any findings above threshold
trivy image --severity CRITICAL --exit-code 1 myapp:latest
Sample output:
myapp:latest (alpine 3.18.4)
=============================================================
Total: 3 (HIGH: 2, CRITICAL: 1)
┌────────────────┬───────────────────┬──────────┬───────────────────┬───────────────────┬──────────────────────────────┐
│ Library │ Vulnerability ID │ Severity │ Installed Version│ Fixed Version │ Title │
├────────────────┼───────────────────┼──────────┼───────────────────┼───────────────────┼──────────────────────────────┤
│ openssl-libs │ CVE-2024-XXXXX │ CRITICAL │ 3.1.3-r2 │ 3.1.4-r0 │ Memory corruption in... │
│ libexpat │ CVE-2023-XXXXX │ HIGH │ 2.5.0-r0 │ 2.5.0-r1 │ XML parsing vulnerability │
└────────────────┴───────────────────┴──────────┴───────────────────┴───────────────────┴──────────────────────────────┘
The Fixed Version column is key. If there’s a fixed version available, rebuilding with an updated base image resolves the finding.
SBOM Generation
Trivy can export the full SBOM in industry-standard formats:
# Generate SBOM in SPDX format
trivy image --format spdx-json --output sbom.json myapp:latest
# Generate in CycloneDX format (widely supported)
trivy image --format cyclonedx --output sbom.json myapp:latest
SBOMs are useful for supply chain compliance, audit trails, and for scanning the same artifact in multiple tools without re-pulling the image.
CI Integration
In GitHub Actions:
name: Build and Scan
on:
push:
branches: [main]
pull_request:
jobs:
build-and-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build 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"
ignore-unfixed: true
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: "trivy-results.sarif"
ignore-unfixed: true filters out CVEs that have no available fix yet; these show up in reports but you can’t act on them. Limiting to actionable findings reduces noise and makes the reports useful for triage.
The SARIF upload sends findings to GitHub’s Security tab, where they appear alongside code scanning alerts.
Snyk Container
Snyk offers both a free tier and a commercial product. Its container scanning goes slightly further than Trivy in a few areas: Snyk’s vulnerability database is proprietary and often has more context about exploitability, and its fix suggestions are more opinionated.
# Install Snyk CLI
npm install -g snyk
# Authenticate
snyk auth
# Scan a container image
snyk container test myapp:latest
# Monitor ongoing (report to Snyk dashboard)
snyk container monitor myapp:latest
Snyk also integrates with container registries. Connect Snyk to your ECR or Docker Hub account and it monitors pushed images continuously, alerting when new CVEs are published against packages already in your images, without requiring a new push.
The free tier supports scanning up to 200 container images per month, which covers most small teams. Paid plans add team features, policy enforcement, and more complete remediation guidance.
Acting on Findings
Three categories of findings require different responses:
Fixed version available, OS package: Rebuild the image with an updated base tag. If your Dockerfile pins FROM node:22.4-alpine3.19, bump to the current tag. Set a monthly reminder or use Renovate/Dependabot to automate base image updates.
Fixed version available, language package: Update the dependency in your package.json, requirements.txt, or equivalent. Treat it like any other dependency update.
No fix available: Assess risk and document the accepted risk. Some vulnerabilities are in code paths your application never executes. Some are theoretical attacks that require physical access. ignore-unfixed: true in Trivy filters these out; Snyk has a similar --exclude-base-image-vulns flag for base-image-specific findings when no fix exists.
The workflow that makes this sustainable:
- Block on CRITICAL findings with available fixes before merging
- Track HIGH findings in a backlog, address them on a regular cycle
- Accept and document MEDIUM/LOW findings that are unfixable or genuinely unexploitable
Trying to get to zero findings on every commit creates alert fatigue. A realistic threshold that the team can maintain is better than a perfect one that gets bypassed.
Scanning for Secrets
Both Trivy and Snyk can detect secrets baked into image layers. A common mistake: running RUN echo $API_KEY > /app/.env in a Dockerfile stores the key in a layer even if a later step removes the file. Docker layers are additive, so removing a file in a later layer doesn’t erase it from earlier layers.
# Trivy secret scanning
trivy image --scanners secret myapp:latest
If secrets are found, the fix requires rebuilding without the secret in any layer. Use --mount=type=secret (BuildKit) or environment variables set at runtime, not build time.
Kubernetes Admission Control
For teams running Kubernetes, admission webhooks can block the deployment of images that fail scanning. Trivy Operator installs into your cluster and provides a CRD-based interface:
# Automatic scanning of all pods
apiVersion: aquasecurity.github.io/v1alpha1
kind: VulnerabilityReport
metadata:
name: myapp-deployment
spec:
scanner:
name: Trivy
components:
- kind: Deployment
name: myapp
Policies can block pods with CRITICAL vulnerabilities from being scheduled. This adds a runtime gate after the CI gate, which is useful in environments where images might be pulled from external registries or deployed via GitOps tooling outside your main pipeline.
The Minimal Setup Worth Having
If you’re starting from zero, the setup that provides most of the value in the least time:
- Add
trivy image --severity CRITICAL --exit-code 1 --ignore-unfixedto your CI pipeline after the Docker build step. Blocks CRITICAL-severity CVEs with available fixes. - Set up Renovate or Dependabot for base image updates. Keeps the OS layer current automatically.
- Use
--mount=type=secretfor any credentials needed at build time.
This doesn’t cover everything. It doesn’t monitor already-deployed images for new CVEs, doesn’t enforce policies at the Kubernetes admission layer, and doesn’t catch misconfiguration risks. But it catches the most common and most exploitable class of issues: known vulnerabilities in outdated base images, with minimal setup cost.
From there, extend as your security requirements grow.
Sponsored
More from this category
More from Cybersecurity
Sponsored
Discussion
Join the conversation.
Comments are powered by GitHub Discussions. Sign in with your GitHub account to leave a comment.
Sponsored