Your production container image has 200+ known vulnerabilities. On average, 12 of them are critical. And you deployed it last Tuesday without scanning it.
This isn't hypothetical. A 2025 study by Sysdig found that 87% of container images in production contain at least one high or critical vulnerability. The problem isn't awareness — every team knows they should scan. The problem is that container scanning has been too complex, too slow, and too noisy to fit into real development workflows.
This guide fixes that.
What Container Scanning Actually Checks
Container image scanning analyzes every layer of your Docker/OCI image:
OS Package Vulnerabilities
The base image (Ubuntu, Alpine, Debian) ships with hundreds of system packages. Each package version is checked against the NVD and distribution-specific vulnerability databases.
Language Dependency Vulnerabilities
Your application's dependencies — package.json, requirements.txt, go.sum, Gemfile.lock — are scanned for known CVEs. This catches the majority of exploitable vulnerabilities because application dependencies change more frequently than OS packages.
Configuration Issues
Misconfigurations like running as root, exposing unnecessary ports, or using latest tags instead of pinned versions. Tools like Dockle check against CIS Docker Benchmark standards.
Secrets and Credentials
Accidentally embedded API keys, database passwords, or private certificates. A single committed secret in a Dockerfile layer can persist even after deletion because Docker layers are immutable.
License Compliance
Identifying open-source licenses (MIT, GPL, Apache) in your dependency tree. Critical for organizations with legal requirements around open-source usage.
The Modern Scanner Stack
Trivy (Aqua Security)
The most widely adopted container scanner. Trivy scans OS packages, language dependencies, IaC files, and Kubernetes manifests.
Strengths: Fast (30-second scan for most images), comprehensive vulnerability database, excellent CI/CD integration, scans more than just containers.
Best for: Your primary container scanner. Start here.
Grype (Anchore)
Focused vulnerability scanner with strong SBOM integration. Pairs with Syft for software bill of materials generation.
Strengths: Accurate vulnerability matching, low false-positive rate, SBOM-native workflow.
Best for: Organizations that need SBOM compliance (Executive Order 14028, FedRAMP).
Syft (Anchore)
Not a vulnerability scanner — Syft generates SBOMs. It catalogs every package in your image with version, license, and source information.
Output formats: SPDX, CycloneDX, JSON, Table.
Best for: SBOM generation and software supply chain visibility.
Dockle
Dockerfile and image best-practice checker. Validates against CIS Docker Benchmark.
Checks: Running as non-root, no sensitive files in image, proper HEALTHCHECK, avoid latest tag.
Best for: Dockerfile quality gates in CI/CD.
Scanning Workflow: From Build to Production
Phase 1: Build-Time Scanning
Scan images in your CI/CD pipeline immediately after docker build:
# GitHub Actions example
name: Build image
run: docker build -t myapp:${{ github.sha }} .
name: Scan with Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
severity: CRITICAL,HIGH
exit-code: 1 # Fail the pipeline
Policy: Block deployment if critical or high vulnerabilities with EPSS > 10% are detected.
Phase 2: Registry Scanning
Scan images in your container registry (ECR, GCR, ACR, Docker Hub) on a schedule. This catches newly disclosed CVEs in already-pushed images.
Frequency: Daily scan of all production-tagged images.
Phase 3: Runtime Monitoring
Continuously monitor running containers for known vulnerabilities. When a new CVE is published that affects a package in your running image, get alerted immediately.
Tools: Falco (runtime security), Kubeclarity (runtime SBOM), Vulnios (continuous scanning with alerting).
Multi-Engine Scanning: Why One Scanner Isn't Enough
Every scanner has different vulnerability database coverage, matching algorithms, and advisory sources. Running multiple scanners catches more vulnerabilities:
| Scanner | Primary Database | Unique Strength |
|---------|-----------------|-----------------|
| Trivy | NVD, GHSA, OS distro advisories | Broadest coverage, fastest |
| Grype | NVD, GHSA, vendor advisories | Lowest false-positive rate |
| Clair | NVD, OS distro advisories | Red Hat/CoreOS focused |
Vulnios approach: Run Trivy, Grype, AND Syft simultaneously, then deduplicate findings across engines. You get the union of all scanner coverage with a single unified results view.
Base Image Selection: Your Most Important Security Decision
Your base image choice determines 60-80% of your vulnerability count before you even add application code.
Tier 1: Minimal (Recommended)
alpine:3.19 — 5MB, ~5 CVEsgcr.io/distroless/static — 2MB, ~0 CVEsscratch — 0MB, 0 CVEs (for Go/Rust static binaries)Tier 2: Slim
debian:bookworm-slim — 80MB, ~20 CVEsubuntu:24.04 (minimal) — 78MB, ~15 CVEsTier 3: Full (Avoid in Production)
ubuntu:24.04 — 188MB, ~50+ CVEsnode:20 — 350MB, ~100+ CVEsRule: Use the smallest base image that supports your application runtime.
SBOM: From Nice-to-Have to Required
A Software Bill of Materials (SBOM) lists every component in your software — like a nutrition label for code. SBOMs are now required by:
Generate SBOMs automatically with every build:
# Generate SBOM with Syft
syft myapp:latest -o spdx-json > sbom.spdx.json
# Scan SBOM for vulnerabilities with Grype
grype sbom:sbom.spdx.json
Kubernetes-Specific Scanning
For K8s clusters, extend scanning beyond images:
Getting Started with Vulnios
---
Scan your first container image at vulnios.com. Multi-engine scanning, SBOM generation, and EPSS prioritization — all in one platform.
Ready to secure your organization?
Start scanning with 32 security engines — free tier available.
Get Started Free