Scanning Docker images: where build security breaks
A container almost entirely consists of someone else's code: your service is a dozen files, while the base layer brings hundreds of packages. Vulnerabilities in containers live exactly there, not in application code.
The same base layer is reused by dozens of services, so a single finding instantly multiplies across the entire image registry.
The second loss area is transitive dependencies: a direct library has been updated, but its third-level dependency remains on a vulnerable branch.
According to independent data from Snyk, a significant share of built images contains at least one known vulnerability, and most such findings do not come from the team's code.
The third area is process. One-off Docker image scanning before release looks at a single build, not the entire registry: the report is read by eye, interpreted differently, and postponed until the next release.
While the check sits after the build, a fix shifts the release date, and without a criticality policy, 'nothing to fix' and 'no time to fix' look the same.
As a result, build security rests on a person, not on a rule. What follows is how to move checks into an automated loop: from choosing the base layer to blocking the build by criticality level while there is still something to fix.
What changes after implementing checks in the build pipeline
What we measure: time to fix and share of clean builds
The check is embedded directly in the build step: the finding appears in the same report as the build log — before the tag is assigned and the image is sent to the registry.
The developer sees which package or base layer pulled in the vulnerability and fixes the dependency in the same branch. Time to fix drops from weeks to hours: the fix arrives together with the code that caused it.
The second metric is the share of builds without critical findings. The threshold is set by policy: critical and high levels block publication, low goes into the report without stopping the build.
Growth in this share shows that the team is fixing causes, not sorting through a backlog before release.
Vulnerability classes that leave the release flow
Regular scanning with Snyk Container cleans out what previously reached production: outdated packages in the base image, libraries with publicly known vulnerabilities, and debug utilities left over from the parent layer.
Separately, a class of reproducibility problems is closed: the image is built from a pinned base layer, not from 'latest', which changes between builds.
The nature of pre-release work also changes. Manual pre-release checks — eyeballing dependencies — are replaced by an automated verdict: the build passes or comes back with a specific list of fixes.
A findings register instead of scattered reports
Results are collected into a single findings register with history: when the vulnerability appeared, who closed it and when, and what remains open.
Recurring findings are collapsed, so the list does not grow with the number of builds, and base image updates are planned by accumulation rather than by calendar.
Scan modes: at build time, in the registry, and on a schedule
The scan mode is chosen not by tool convenience but by two questions: who authored the image and how long it lives in production.
In-house builds are logically checked at the build stage, third-party and base layers in the registry, and what is already deployed has to be rechecked on a schedule: vulnerabilities in dependencies appear after release too.
Snyk Container supports all three modes — the only difference is which one is enabled and with what criticality threshold.
| Option | When it fits | Limitations |
|---|---|---|
| Build-time scanning | In-house images, build step in the pipeline | Lengthens the build; does not see vulnerabilities found after it |
| Registry scanning | Third-party and base images, mirrors, manual uploads | Triggers after publication; requires registry permissions and policies on the storage side |
| Scheduled scanning | Already deployed releases, long-lived versions | A blind window remains between runs; report routing to owners is needed |
The criticality threshold is the second parameter that determines whether the scheme works at all. Without it, the build turns red on every transitive dependency, the team gets used to ignoring the report, and the check becomes a formality.
Usually the build is blocked on critical and high levels with confirmed exploitability; the rest goes into scheduled updates. The threshold is set once in policy and inherited by all modes.
How the modes combine in a single pipeline
The three modes cover different windows rather than duplicating each other: the build gives a fast fail before publication, the registry insures artifacts that arrived outside the pipeline, and the schedule rechecks what has already been put into operation.
Bringing them into one scheme means maintaining a single criticality threshold and a single notification channel; otherwise the same vulnerability arrives in three notifications, and people stop reading them.
Implementation order: from pipeline audit to handing over procedures
Work proceeds in iterations: first we capture the current state of the pipeline, then enable rules in monitoring mode so as not to stop releases, and only after that switch checks to blocking mode.
This order provides a clear rollback point at each step and does not turn implementation into a one-time 'emergency brake' for the whole team.
Pipeline audit and base layer inventory
- We inspect the pipeline: collect the list of builds, registries, and branches from which images reach the production environment.
- We inventory builds and base layers — how many different parents are used, which have gone the longest without updates.
- We identify critical services and agree on thresholds: what we consider a blocking vulnerability and what goes into the report without stopping the build.
- We embed Snyk Container scanning at the stage where the image is already built but not yet sent to the registry.
Acceptance: how we confirm the result
- We configure rules: policies by criticality level, exceptions with justification and review deadline, and notifications to owners.
- We test on real builds: what share of images the check covers, how many false positives there are, and how much time it adds to the build.
- We hand over procedures — policy descriptions, the response order for a block, and instructions for developers.
Configurations, blocking policies, and image documentation
The scan result by itself is a list of findings, not a process. The customer receives a set of artifacts: rule configurations, blocking policies, the flow of images across environments, and security documentation that the on-call engineer can follow without our prompting. Below is what specifically remains in the repository and the internal knowledge base.
Rule configurations and criticality thresholds
- Blocking policies in Snyk: which criticality levels stop the build and which go into the report without hindering release.
- Rule configuration files in the service repository — changes go through review and are versioned together with code.
- A suppression list with a mandatory review deadline: an exception without a date does not pass the check and goes into the report.
- Separate profiles for build and production environment: a soft threshold during development, a strict one at cluster ingress.
Response procedure for findings in the production environment
- On-call actions by level: what is closed unscheduled and what goes into a planned release.
- The rollback rule to the previous image and the rebuild procedure without the vulnerable dependency.
- Escalation point and notification template: it is clear who receives the message and within what time they respond.
- Acceptance checklist for a new rule: how to make sure the block does not break the release before enabling it.
Case study: how the share of releases with critical vulnerabilities was reduced
Before implementation, images were built in the build pipeline, while vulnerability checking was a separate manual step: an engineer ran a scan across the list of services once every two weeks and reviewed the output by eye.
Critical findings surfaced only after release — when rollback costs more than fixing the base layer — and some comments related to packages that are not used at runtime at all.
We embedded Snyk Container directly into the build: the check runs on every built image, the policy blocks release on a critical finding with an available fix, and the rest goes into the report with a reference to the layer and package. We separately configured ignore rules for unsupported paths — they eliminated most of the noise.
Metrics before and after implementation
| Metric | Before | After |
|---|---|---|
| Share of releases with critical findings | every third | one in twenty |
| Time to check one image | 30–40 minutes of manual review | 2–3 minutes as a build step |
| Volume of manual review | the entire output | only critical issues with a confirmed fix |
| Recheck after a fix | not tracked | automatically on the same layer |
The figures were consolidated from the Project Report: the developer receives the finding in the same run where the build failed and sees the specific package, not a list of hundreds of lines.
Therefore, the reduction in the number of findings did not come from weakening the rules — the criticality threshold remained the same.
How does Snyk Container differ from built-in registry scanning?
Built-in registry scanning triggers at the moment of image publication and works with the completed manifest.
It sees top-level packages and the system layer but does not expand the dependency tree — transitive libraries pulled in by the build tool often remain outside the report. The image goes into the production loop with a vulnerability for which the registry showed a clean result.
A specialized scanner parses the image layer by layer, including intermediate layers, and reconstructs the full dependency graph from package manager manifests and lock files.
This is the depth of dependency analysis: it shows not only the vulnerable package itself but also the chain by which it entered the image — which component pulled it in and which layer it resides on.
A separate difference is working with base layers. Registry scanning evaluates each tag as a standalone object, so updating the parent image does not re-evaluate derivatives.
A container scanner maintains the relationship between parent and descendants: a fix in the base layer immediately marks which images are closed by a single rebuild.
False positives are the main argument against scanning by version list. A package is declared vulnerable regardless of whether its code is called, whether a ready fix exists, and whether an exploitation path is reachable.
A specialized tool accounts for this and divides findings by priority: what remains in the queue is what can actually be closed, not hundreds of items that an engineer filters manually.
Practical conclusion: built-in registry checks are a rough barrier at storage ingress, while a specialized scanner is analysis at the build and maintenance stage. Scanner comparison ends with choosing a control point, not choosing one tool forever.
Limitations of the approach and common doubts before starting
Image scanning is embedded in the build pipeline, so the same doubts come up before starting: will the check slow down release, what access to the registry is needed, what to do about false positives and third-party base layers.
Will the check slow down the build?
Analysis proceeds layer by layer and reuses cache: only the changed layer is parsed. Seconds are added to the build; the full analysis runs in parallel and does not delay release. On release branches, the check blocks release; on working branches, it only warns.
What access to repositories is needed?
Read-only access is required: the service account sees the image registry and dependency manifests; tokens are stored separately. If the registry is closed behind a perimeter, we deploy the scanner inside the network. The first service is connected within a few days, the rest in iterations.
What to do about false positives?
A vulnerability in a library is not always reachable from code, so we prioritize by reachability and criticality rather than by the overall finding count.
We document exceptions explicitly: justification, owner, review deadline — the report stays short, and disputed findings do not silently accumulate.
What to do about third-party base layers?
The base image is someone else's responsibility, but the risk remains ours. We move to minimal base images and rebuild services on the current branch, and in policy we separate: critical issues from the base block release; the rest is a task with a clear deadline.
Let's discuss the task and assemble a scanning scheme for your pipeline
We start the conversation with your pipeline, not with general recommendations: how many services are built, how often releases go out, where images are currently checked manually, and who reviews findings.
This determines at what stage Docker image scanning becomes a blocking step, where a report is sufficient, and what threshold by vulnerability levels to set so that checks catch dangerous issues but do not stop release.
We configure on Snyk Container — the set of checks and exception rules are still assembled for your build stack, not taken from a standard profile.
After the discussion, we send:
- a scheme for embedding checks into the pipeline: where scanning runs, what blocks the build, and what only goes into the report;
- a policy by finding levels — what we consider critical, what we allow temporarily and under what conditions;
- an exception procedure: who postpones a fix, for how long, and how that decision is reviewed;
- an estimate of the scope of work by stages — configuration, embedding into the build, team training, and rollout;
- a pilot plan on one service with success criteria, metrics, and a rollback condition if checks hinder release;
- a documentation set and configuration examples for your stack.
Describe the task — we will come back with an estimate and a plan. If it is more convenient to start with one service, that is what we will propose: rollout will follow an already proven scheme.







