Scan Django Docker Images for Vulnerabilities With Trivy

Scan Django container images with Trivy for vulnerable OS packages and Python dependencies: run it locally and in CI, fail builds on fixable critical issues, ignore false positives with expiry, rebuild on new base images, and scan running registries.

Problem statement

A Django image contains far more than your code: a Debian or Alpine userland with OpenSSL, libxml2, and image libraries, plus every Python package from your lock file. New vulnerabilities are published daily for those components. An image built six months ago and never rebuilt carries six months of known CVEs. Trivy, an open-source scanner, finds vulnerable OS packages and Python dependencies in images, filesystems, and lock files, and fits into CI so known-vulnerable images do not ship and old ones get rebuilt.

Scanning images with TrivyBuild imageCI or localtrivy imageOS + Python depsPolicyfail on criticalsRebuild oftennew base, new fixesScan every build and rebuild regularly; most fixes arrive through base images and lock updates.
  1. Run trivy image --severity HIGH,CRITICAL --ignore-unfixed myapp:latest locally to see the current state.
  2. Add Trivy to CI after the image build; fail the job on fixable CRITICAL (and optionally HIGH) findings.
  3. Scan the lock file early with trivy fs --scanners vuln . to catch Python issues before building.
  4. Record accepted risks in .trivyignore with a reason and review date.
  5. Rebuild images at least weekly from the latest base image, even without code changes.
  6. Use slim base images and multi-stage builds to reduce the number of packages to patch.

Step-by-step solution

1. Scan locally

trivy image --severity HIGH,CRITICAL --ignore-unfixed ghcr.io/you/myapp:3f2a1c9

The report groups findings by target: the OS packages (from debian:bookworm-slim or similar) and Python packages found in site-packages. --ignore-unfixed hides vulnerabilities with no available fix, which you cannot act on yet.

2. Fail CI on fixable critical issues

# GitHub Actions
- name: Build image
  run: docker build -t myapp:${{ github.sha }} .
- name: Trivy scan
  uses: aquasecurity/trivy-action@0.28.0
  with:
    image-ref: myapp:${{ github.sha }}
    severity: CRITICAL,HIGH
    ignore-unfixed: true
    exit-code: "1"
    format: table

Start with exit-code: 0 and SARIF upload to GitHub's security tab to see the baseline, then enforce. Failing on every HIGH from day one usually blocks deploys over issues that need a base image update rather than code changes.

what to do with each findingFindingActionOS package, fix availablerebuild on the latest base imagePython package, fix availablebump the lock file, run testsno fix available yettrack; mitigate if exploitablenot reachable in your use.trivyignore with reason and expirybuild tools in final imagemulti-stage build, drop themMost findings disappear with a fresh base image and dependency updatesdjango-deployment.com
Prioritise criticals in packages your app actually uses at runtime.

3. Scan dependencies before building

trivy fs --scanners vuln --severity HIGH,CRITICAL .

This reads requirements.txt, poetry.lock, or uv.lock and flags vulnerable Python packages in pull requests, before an image exists. It complements tools like pip-audit and Dependabot alerts.

4. Manage exceptions

# .trivyignore
# CVE in libtiff via Pillow wheel; we do not accept TIFF uploads. Review 2026-12-01.
CVE-2025-12345

Use .trivyignore.yaml to add expiry dates so exceptions resurface. Every ignore needs a reason a reviewer can check.

5. Rebuild regularly

Base images receive security updates continuously. Schedule a weekly CI job that rebuilds with --pull --no-cache (or at least --pull), scans, runs tests, and deploys if green. Pin base images by digest for reproducibility and let Renovate or Dependabot propose digest updates.

6. Shrink the attack surface

Multi-stage builds keep compilers and headers out of the runtime image; python:3.12-slim has far fewer packages than the full image; distroless images go further. Fewer packages mean fewer findings and faster scans.

image scanning that stays usefulTrivy in CI after every buildfail on fixable criticalsLock file scanned in pull requeststrivy fs or pip-auditIgnores documented with expiry.trivyignore.yamlWeekly rebuild on fresh base--pull, tests, deploySlim, multi-stage runtime imagefewer packages to patchScanning finds problems; rebuilding regularly is what fixes themdjango-deployment.com
Also scan images already in your registry; new CVEs affect old images.

Verification check: the CI job shows a Trivy report for each build, a deliberately old base image fails the gate, and the weekly rebuild job produces images with fewer or no fixable critical findings.

Explanation

Trivy identifies the packages inside an image by reading OS package databases and Python package metadata, then matches name and version against vulnerability databases from distributions and advisories (including GitHub Security Advisories for Python). Because most vulnerabilities in a Django image live in the base OS and third-party packages rather than your code, the effective remedies are updating base images and dependencies. Integrating the scan into CI turns that into routine work with a clear policy, instead of an occasional audit.

Edge cases / notes

  • False positives happen when distribution backports fix a CVE without changing the upstream version; Trivy uses distro advisories to reduce these.
  • Alpine vs Debian. Alpine has fewer packages but uses musl; scanner coverage is good for both.
  • Registry scanning. GHCR, ECR, and Artifact Registry offer built-in scanning; Trivy can also scan registry images on a schedule.
  • SBOMs. trivy image --format cyclonedx produces a software bill of materials for compliance.

Next steps

FAQ

What does Trivy scan in a Django image?

OS packages from the base image and Python packages installed in the image, plus misconfigurations and secrets if enabled.

Should CI fail on every vulnerability?

Start by failing on fixable criticals, then tighten as the baseline improves.

How do I fix vulnerabilities in the base image?

Rebuild from the latest version of the base image; many fixes arrive without code changes.

How do I ignore a false positive?

Add it to .trivyignore or .trivyignore.yaml with a reason and an expiry date.

Keep going