Split the pipeline in two. Fetch and verify dependencies in one step that has network access, then run compile and test in a second step that has none. A container started with the no network option, plus each tool's offline setting, makes any surprise download fail loudly.
Why this matters for security
Many supply chain attacks run at build time. A package script, a build plugin or a compromised tool downloads and runs something. If the build step has no network, a whole class of that behavior fails, and a failure is a signal you can read. You also get offline resilience as a side effect, because a build that does not need the network is not hurt by a registry outage.
Step one: fetch in a controlled stage
The fetch stage is where the network is allowed, so keep it narrow.
- Install from the lockfile with a strict install command.
- Point it at your mirror, not the public registry, where you can.
- Put the downloaded packages where the next stage can read them: a vendor directory, a cache volume or an artifact.
- Record what was fetched.
Step two: build with the network off
Docker's documentation says you can completely isolate a container's networking stack with the none network option when starting it. Inside, only a loopback device exists. Run the build in a container started that way, with the fetched dependencies mounted in.
docker run --rm --network none \
-v "$PWD":/src -w /src build-image:pinned \
sh -c "npm ci --offline && npm test"
Then add the tool level switches as a second line of defense. Each is documented by its project:
| Tool | Setting | Documented effect |
|---|---|---|
| npm | offline | No network requests during install |
| cargo | net.offline | Avoids the network, uses locally cached data |
| Go | vendor mode | Does not use the network or the module cache |
| Go | GOPROXY off | Disallows downloading modules from any source |
| pip | no-index with find-links | Ignores the package index, uses given locations |
The tool settings fail with a clear message. The container setting is stronger, because it also blocks things the tool does not control, such as a script that calls out with its own HTTP client.
What you will find
Expect the first run to break. Common causes:
- Install scripts that download binaries. Decide whether to vendor the binary, replace the package, or block scripts. See npm ignore scripts in CI.
- Tests that call real services. They should use fakes or a test network that you control.
- Build tools fetched on the fly. Pin and bake them into the build image.
- Telemetry or update checks. Turn them off, since they are noise in an isolated build.
Each failure is a hidden dependency you can now see. Fix the cheap ones and write down the rest.
Where this does not help
Network isolation does not stop a malicious dependency from tampering with the build output using local access. It also does not replace review of what you fetched. Combine it with lockfile discipline and a vendoring or mirror strategy, and use monthly offline build drills to keep it working.
Where Safeguard fits
The fetch stage is the right place for a check. Safeguard's dependency scanning, policies and gates can scan the fetched set before the offline build starts and block it if it breaks your rules.
Quick check
Will an offline build break every project?
Often the first run breaks something, usually an install script or a tool fetched at build time. Each failure is worth fixing.
Is the tool offline flag enough without the container setting?
It catches the tool's own downloads. A script using its own network client is only blocked by container or host level isolation.
Sources
- Docker Docs: None network driver
- npm config reference
- Cargo configuration reference
- The Go Modules Reference
- pip install reference
Last reviewed: 19 October 2026.