I Ran Trivy on Every Container in My Homelab — The Results Were Embarrassing

Last weekend I had a quiet Saturday morning and made the mistake of running trivy image against every container in my homelab. I have 47 containers running on TrueNAS. I expected maybe a handful of medium-severity CVEs. What I got was 312 critical vulnerabilities across 23 containers.

Here’s what I found, what I fixed, and the automated scanning pipeline I built so this never sneaks up on me again.

The Initial Scan: A Reality Check

Trivy is a free, open-source vulnerability scanner from Aqua Security. It scans container images, filesystems, and git repos. Installation is one line:

curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin

I wrote a quick bash loop to scan every running image:

docker ps --format "{{.Image}}" | sort -u | while read img; do
  echo "=== $img ==="
  trivy image --severity CRITICAL,HIGH --quiet "$img"
done

The output was grim. My worst offenders:

  • node:16-alpine (used by 4 containers) — 43 critical CVEs. Node 16 went EOL in September 2023. I was running a 3-year-dead runtime.
  • python:3.8-slim — 28 critical CVEs including a libexpat remote code execution (CVE-2024-45491)
  • nginx:1.21 — HTTP/2 rapid reset vulnerability (CVE-2023-44487) still unpatched
  • postgres:13 — multiple privilege escalation paths

The common thread: I’d deployed these containers months (or years) ago and never updated them. They worked, so I forgot about them. Classic homelab syndrome.

Trivy vs Grype vs Docker Scout: Which Scanner Actually Works?

Before automating anything, I tested three scanners against the same image (node:18-alpine) to compare results:

Scanner CVEs Found Scan Time DB Size False Positives
Trivy 0.52 47 8.2s ~40MB 2
Grype 0.79 44 6.1s ~130MB 1
Docker Scout 51 12.4s Cloud 5

All three found the same critical issues. Trivy found slightly more because it also scans language-specific packages (npm, pip) inside the image, not just OS packages. Grype was faster but missed some application-level dependencies. Docker Scout flagged the most but had more noise — it flagged a few CVEs in packages that weren’t actually reachable in my configuration.

I went with Trivy because it’s the most complete out of the box and the JSON output is clean for automation.

Building an Automated Scan Pipeline

Running manual scans is useless if you forget to do it. Here’s the cron-based setup I built:

#!/bin/bash
# /opt/scripts/scan-containers.sh
REPORT_DIR="/opt/reports/trivy"
mkdir -p "$REPORT_DIR"
DATE=$(date +%Y-%m-%d)

docker ps --format "{{.Image}}" | sort -u | while read img; do
  SAFE_NAME=$(echo "$img" | tr '/:' '__')
  trivy image --format json --severity CRITICAL,HIGH     --quiet "$img" > "$REPORT_DIR/${SAFE_NAME}_${DATE}.json"
done

# Count criticals
CRITS=$(cat $REPORT_DIR/*_${DATE}.json |   python3 -c "import json,sys;   total=sum(len(r.get('Vulnerabilities',[]))   for f in sys.stdin for r in json.loads(f.read()).get('Results',[]));   print(total)")

if [ "$CRITS" -gt 0 ]; then
  curl -s -d "Found $CRITS critical/high CVEs across containers"     ntfy.sh/your-alerts-topic
fi

This runs daily at 6 AM via cron. If any critical or high CVEs appear, I get a push notification. The JSON reports accumulate so I can track trends — am I getting better or worse over time?

The Fix Strategy: Prioritize by Exposure

312 CVEs sounds terrifying, but not all vulnerabilities are equal. I prioritized based on three factors:

  1. Network exposure — Is this container reachable from the internet? My reverse proxy (nginx) and Gitea instance were top priority.
  2. Data sensitivity — Containers touching personal data or credentials got fixed next.
  3. Exploit availability — Trivy flags whether a public exploit exists. CVEs with known exploits jump the queue.

The actual fixes were boring but effective:

  • Updated 12 base images to current versions (node:22-alpine, python:3.12-slim, nginx:1.27)
  • Pinned image digests instead of tags in my docker-compose files — nginx:1.27@sha256:abc123... prevents silent tag mutations
  • Deleted 8 containers I wasn’t even using anymore. If it’s not running, it can’t be exploited.
  • Added --read-only filesystem flags to 15 containers that had no business writing to disk

Total time: about 4 hours spread across two evenings. My critical CVE count dropped from 312 to 7 — and those 7 are in packages awaiting upstream patches with no public exploits.

What I’d Do Differently

If I were setting up a homelab today, I’d do three things from day one:

Pin everything. Never use :latest. Never use bare version tags. Use full digests. Yes, it’s more work when updating. That’s the point — updates become intentional, not accidental.

Scan on pull. Add a pre-deploy hook that runs Trivy before any new image goes live. Block deployment if critical CVEs exist. This takes 10 seconds per image and prevents the backlog from growing.

Use distroless or Alpine. My python:3.8-slim had 28 CVEs. A distroless Python image for the same app? 3 CVEs, all low severity. Smaller attack surface means fewer things to patch.

My Current Setup: Hardware That Makes This Painless

Running 47 containers plus daily vulnerability scans needs decent hardware. I’m using a TrueNAS box with 64GB ECC RAM — scanning all images in parallel takes about 2 minutes. If you’re building or upgrading a homelab server, ECC RAM matters when you’re running this many services. I’ve had good results with the Kingston Server Premier 32GB DDR4 ECC (affiliate link) — two sticks give you 64GB with room for ZFS caching.

For storage, container images eat disk fast. A decent NVMe for your Docker storage pool makes both pulls and scans noticeably faster. The Samsung 980 Pro 2TB (affiliate link) has been solid in my setup for two years with heavy container churn.

The Bottom Line

If you haven’t scanned your containers recently, do it today. It takes 5 minutes and the results will probably surprise you. Trivy is free, fast, and the output is actionable.

The real lesson: security debt compounds silently. A container that was fine when you deployed it 18 months ago might have 40+ critical CVEs today. Automated scanning turns an invisible problem into a visible one, and visible problems get fixed.

For more tools and security workflows, check out our DevSecOps guide and the homelab security guide.


Want daily market intelligence with the same no-fluff approach? Join Alpha Signal for free — actionable signals, no hype.

📧 Get weekly insights on security, trading, and tech. No spam, unsubscribe anytime.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

Also by us: StartCaaS — AI Company OS · Hype2You — AI Tech Trends