TecLeads TecLeads Blog
2026-07-20 · 7 min read

Trivy Works Best When You Stop Treating Every Scan the Same

Dimly lit developer workstation with code on screen
trivydevsecopscontainer-securityinfrastructure-as-codeopen-source

The deployment had passed its container vulnerability scan. The image contained no critical findings with an available fix, so the pipeline was green. Ten minutes later, somebody noticed the Kubernetes manifest still ran the container as root and mounted a service account token it did not need.

Nothing was wrong with the scanner. We had simply asked a narrow question and treated the answer as a security verdict.

This is where Trivy earns a place among the DevOps tools I keep around. It can inspect container images, local filesystems, Git repositories, infrastructure as code, and exposed secrets. The useful bit is not that one binary has many subcommands. Plenty of tooling has an ambitious feature table. The useful bit is that teams can apply a reasonably consistent scanner at several points in the delivery path.

The catch is that one scanner does not mean one policy.

One binary, three different jobs

Trivy follows a target and scanner model. The target is what you are inspecting, such as an image or filesystem. The scanners say what to look for, including vulnerabilities, secrets, and misconfigurations. The official first steps documentation explains the model, but I recommend specifying scanners explicitly in automation. Defaults change, and nobody enjoys working out why CI behaved differently after a version update.

For a repository containing application dependencies, Terraform, Kubernetes YAML, and Dockerfiles, start with:

trivy fs \
 --scanners vuln,secret,misconfig \
 --severity HIGH,CRITICAL \
 .

That command is useful during development because it catches several classes of mistake before an image exists. Trivy can inspect lock files for known vulnerable dependencies, check infrastructure definitions, and search plaintext files for credential patterns. Misconfiguration scanning is not enabled by default for filesystem targets, so leaving out --scanners misconfig quietly removes the IaC part of the exercise.

Do not mistake this source scan for an image scan. A repository cannot tell you everything that ended up in the artifact. Package installation, copied build output, base image contents, and an enthusiastic curl | sh step can all change the result.

Scan the thing you will actually deploy

Build the image, push it, then scan the immutable digest. Tags move. Digests do not.

IMAGE_REF='registry.example.com/payments/api@sha256:replace-me'

trivy image \
 --scanners vuln,secret \
 --image-config-scanners misconfig,secret \
 --severity HIGH,CRITICAL \
 --ignore-unfixed \
 --exit-code 1 \
 "$IMAGE_REF"

The image scan covers operating system packages and language packages found in the artifact. Secret scanning walks files in the image, while --image-config-scanners also examines image metadata. That second location matters. Credentials passed through an ENV instruction can survive in image configuration even when the application directory looks clean. Trivy documents the distinction between image files and image metadata.

I use --ignore-unfixed for a blocking image gate when the immediate contract is, “the team must be able to remediate every failure.” It filters vulnerabilities for which the distribution has not published a fix. It does not make those vulnerabilities harmless. Run a separate reporting scan without that flag and send the output somewhere it will be reviewed. Otherwise, “not currently fixable” becomes “never looked at again,” which is how old base images acquire permanent residency.

Also remember that severity is not a universal truth. Trivy generally prefers the operating system vendor’s advisory because vendors backport fixes and assess their own packages. A CVE may therefore carry a different severity from the value somebody saw in another database. That is usually sensible, not evidence that the scanner is broken.

IaC findings need their own gate

A single command that fails on every high or critical result looks tidy. In practice, it gives three unrelated queues to one team and guarantees arguments.

Split the policy by finding type. For example, block high and critical secrets and IaC mistakes on pull requests, but initially block only fixable critical vulnerabilities while teams clean up dependency debt:

trivy fs \
 --scanners secret,misconfig \
 --severity HIGH,CRITICAL \
 --exit-code 1 \
 .

trivy fs \
 --scanners vuln \
 --severity CRITICAL \
 --ignore-unfixed \
 --exit-code 1 \
 .

This is still one open source scanner, installed and maintained once, but the policies match the work. A private key committed to a repository is an incident, not backlog. A Terraform rule may need review by the platform team. A vulnerable library usually belongs to the application owner. Good automation preserves those distinctions.

Trivy supports Terraform, Kubernetes, Helm, Dockerfiles, CloudFormation, and other configuration types. That coverage is handy, but it does not prove deployed cloud resources match the files in Git. It also does not replace SAST, runtime detection, registry controls, or cloud configuration monitoring. If a tool claims to replace all of those, put your hand on your wallet.

Suppressions are code, so review them like code

The fastest way to ruin security scanning is to make every build noisy. The second fastest is to ignore findings with no owner or expiry.

Trivy supports .trivyignore entries and a structured .trivyignore.yaml format. The structured format can record paths, expiry dates, and statements, although the documentation currently marks it experimental. That status is worth noticing before standardising it across hundreds of repositories.

A plain ignore file can still carry an expiry:

# Base image package is not reachable by this service. Review with next base refresh.
CVE-2025-12345 exp:2026-08-15

An ignore is a risk decision. Require a reason, an owner in the pull request, and a date. “CI was red” is not a reason. For secret findings, prefer narrow path or rule exceptions only for deliberate test fixtures. If a real credential reached Git, rotate it. Deleting the line does not delete history, logs, caches, or existing clones.

Keep CI predictable

Trivy downloads vulnerability databases and misconfiguration checks. Cache them between jobs, but do not freeze them indefinitely. A scanner running with an old database produces reassuringly current timestamps on stale knowledge.

Pin the Trivy version used by CI. Pin container actions by digest where the platform supports it. Generate JSON or SARIF for machines, and keep the table output available for engineers diagnosing a failed job. Uploading a report nobody can find is compliance theatre with storage costs.

Do not scan every target in every stage either. Run the repository scan early because feedback is cheap. Scan the built image once it exists. Scan the digest again at promotion if the artifact has waited long enough for advisory data to change. Teams often respond to new tools by putting the maximum command everywhere, then complain that security is slow. That is a pipeline design problem.

If you only do one thing this week

Add an explicit Trivy filesystem scan to pull requests with vuln,secret,misconfig, then inspect the first results before turning on --exit-code 1. Decide which findings block, who owns each class, and how exceptions expire. After that, add the digest based image gate.

The point is not to boast that one scanner covers images, IaC, and secrets. The point is to ask the right question at the right stage, then make sure somebody is responsible for the answer.


TecLeads helps engineering teams ship this kind of thing faster and more safely. If you'd like a second pair of eyes on your setup, book a 30-minute call or explore what we do.

📍 Tech Pulse · today's quick question 🟢 Level: Basic Networking

What does a VPN mainly provide?

Pick an answer to see how other engineers voted.

Want a hand with this?

TecLeads helps engineering teams ship faster and more securely.

Book a 30-minute call

← All posts