"We should have some vulnerability scanning" usually means three different things: scanning hosts on your LAN, scanning the container images your fleet actually runs, and scanning your public-facing web apps. Here's a free, complementary stack that covers all three.
Greenbone Community Edition (OpenVAS-based) deploys via an official Docker Compose bundle in a few commands.
127.0.0.1-only — deliberately LAN-inaccessible out of the box. Opening it up to your
LAN is a real security-relevant change (an admin UI becoming reachable from more places), so treat it as one
— don't just flip the binding without thinking about who else can now reach it.
Once configs appear, a full authenticated scan of your whole subnet is straightforward via the GMP protocol (either the web UI or the CLI tools). A "Full and fast" scan of a typical home subnet takes a few hours — schedule it monthly rather than weekly given the runtime.
Trivy scans container images for known CVEs by name/tag, no need to connect to each host's Docker daemon individually. Run it as a native binary so it can scan arbitrary images without extra setup, and write a small script that scans every image your fleet actually runs.
grep misreports it as a scan failure), and some images have more than one scanned artifact in
the same report (an OS layer plus a separately-tracked binary), so reading only the first table row silently
drops real findings. Scan with --format json and sum with jq instead:
trivy image --severity HIGH,CRITICAL --quiet --format json "$img" > "$img.json"
total=$(jq '[.Results[]?.Vulnerabilities // [] | length] | add // 0' "$img.json")
OWASP ZAP's baseline mode (spider + passive analysis only, no active attack traffic) is safe to run against live production sites, including anything security-sensitive. Run it one-shot per target from its official Docker image.
bash -c containing its own quoted arguments) is a
reliable way to have a variable silently fail to expand where you expect. Write a real script file with
normal, non-nested quoting and copy it over instead.
chmod 777 the output directory before running (or bind-mount with
a matching non-root UID).
One-off manual runs are a good start, but the real value is recurring scans that actually get looked at:
If your monitoring system supports "local checks" (a small script on the monitored host that prints a status line the monitoring agent picks up as a real service with thresholds — Checkmk works this way), this is a clean way to surface scan trends without building a whole separate dashboard: have each scanner write a small summary file, and a local check script read it and print WARN/CRIT based on a threshold you pick.