Scan images and clusters with Trivy

Trivy is the scanner I reach for first. It covers container images, Kubernetes manifests, and live clusters. The useful part is not that it finds CVEs. It is that you can make the output boring enough to gate a deploy.

Image scans

Start with the image you are about to ship, not the whole registry.

trivy image nginx:1.18
trivy image --severity HIGH,CRITICAL nginx:1.18
trivy image --format json --output results.json nginx:1.18

Flags that matter in practice:

  • --severity HIGH,CRITICAL — ignore noise until the high stuff is gone
  • --ignore-unfixed — skip findings with no upstream fix
  • --skip-update — use the cached DB when you are iterating
  • --clear-cache — when the DB looks stale
  • --timeout 10m — large images will otherwise die mid-scan
  • --exit-code 1 — fail the job if anything matches the filter

Scan a saved tarball when the node cannot pull:

docker save --output image.tar nginx:1.18
trivy image --input image.tar

Config and manifests

trivy config .
trivy config deployment.yaml
trivy config --severity HIGH,CRITICAL --format json k8s-manifests/

This catches Dockerfile and YAML mistakes that never show up in an image CVE report: privileged pods, latest tags, missing limits.

Cluster scans

trivy k8s cluster
trivy k8s cluster --report all
trivy k8s cluster --format json
trivy k8s cluster --include-namespaces production
trivy k8s cluster --exclude-namespaces kube-system,kube-public

Narrower targets:

trivy k8s deployment/nginx
trivy k8s pod/nginx-pod
trivy k8s pods --namespace default
trivy k8s cluster --kubeconfig ~/.kube/config

Ignoring known issues

Put suppressions next to the project, not in a one-off flag you will forget.

cat > .trivyignore << EOF
CVE-2021-33574
CVE-2019-18276
EOF

trivy image --ignorefile .trivyignore nginx:1.18

Gate a deploy

if trivy image --severity CRITICAL --exit-code 1 --quiet nginx:1.18; then
  kubectl apply -f deployment.yaml
else
  echo "scan failed"
  exit 1
fi

If the scan cannot talk to the daemon, run it with the same privileges the runtime uses. If the DB will not update, clear the cache and retry before you assume the image is clean.

Severity order is the usual one: CRITICAL, HIGH, MEDIUM, LOW. For a pipeline, scan CRITICAL first. Then HIGH. Do not start with the full table.