Docker Guide · 1 min read

Docker behind a proxy: daemon, build and compose

Docker needs a proxy in three separate places — the daemon that pulls images, the build that runs commands, and the containers you run. Set the wrong one and pulls still fail. Here is each, correctly scoped.

Three layers, three configs

The reason "I set http_proxy but docker pull still fails" is so common is that Docker has three independent network contexts, and your shell's environment variables only touch one of them. First, the daemon (dockerd) is what actually pulls images from a registry — it runs as a system service and reads its proxy from its own config, not your shell. Second, the build runs each RUN step inside a temporary container that needs its own proxy to reach the network. Third, the running container needs a proxy if the app inside it makes outbound calls.

All three use the s4m HTTP endpoint on port 3128 with USER:PASS auth. The modern way to configure the daemon and build is the Docker client config file ~/.docker/config.json, which injects proxy settings into builds and docker run automatically; the daemon itself is configured via a systemd drop-in or Docker Desktop settings. See the proxy page; this pairs naturally with the npm/yarn and pip/conda guides for what runs inside your images.

Configure all three layers

Daemon via systemd drop-in, builds and runs via docker config.json, and a compose fallback. Replace USER:PASS.

bash
# --- 1) DAEMON (image pulls): systemd drop-in on Linux ---
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo tee /etc/systemd/system/docker.service.d/http-proxy.conf >/dev/null <<'CONF'
[Service]
Environment="HTTP_PROXY=http://USER:[email protected]:3128"
Environment="HTTPS_PROXY=http://USER:[email protected]:3128"
Environment="NO_PROXY=localhost,127.0.0.1,.internal"
CONF
sudo systemctl daemon-reload && sudo systemctl restart docker

# --- 2) BUILDS + RUNS: ~/.docker/config.json (injected automatically) ---
cat > ~/.docker/config.json <<'JSON'
{
  "proxies": {
    "default": {
      "httpProxy":  "http://USER:[email protected]:3128",
      "httpsProxy": "http://USER:[email protected]:3128",
      "noProxy":    "localhost,127.0.0.1,.internal"
    }
  }
}
JSON

# --- 3) docker-compose fallback (per-service build args) ---
# services:
#   app:
#     build:
#       context: .
#       args:
#         HTTP_PROXY:  http://USER:[email protected]:3128
#         HTTPS_PROXY: http://USER:[email protected]:3128

Get docker pull and build working

Fix the layers in the order failures usually appear.

Fix the daemon first

If docker pull fails, the daemon can't reach the registry. Add the systemd drop-in with HTTP_PROXY/HTTPS_PROXY, then daemon-reload and restart docker. On Docker Desktop, set the proxy in Settings instead.

Configure the client for builds

Add the proxies block to ~/.docker/config.json so docker build and docker run inject the proxy into each step and container automatically — no Dockerfile edits required.

Add NO_PROXY everywhere

Include localhost, 127.0.0.1 and any internal registry or service host in NO_PROXY at every layer, so internal traffic is not needlessly routed out through the proxy.

Verify each layer

Test with docker pull hello-world (daemon), a build that runs apt-get or pip (build), and a container that curls an IP-echo endpoint (runtime).

Scoping, secrets and honesty

Two cautions. First, keep proxy credentials out of image layers. Setting proxy env vars inside a Dockerfile with ENV bakes them into the image; use build args or the client config.json injection instead, so credentials stay on the build host. Second, be precise with NO_PROXY — a missing entry for an internal registry means Docker tries to reach it through the external proxy and fails confusingly.

s4m's HTTP proxy is an authenticated datacenter endpoint, metered pay-as-you-go — well suited to CI builders and DevOps hosts that must pull images and packages from behind a controlled egress. It is not residential, and it is a forward proxy, not a registry mirror or cache; for pull-through caching, run a registry mirror alongside it. When your build fleet needs one allowlisted egress IP, add a dedicated personal IP. A 407 anywhere means bad proxy credentials — see the 407 guide. Validate the endpoint with the tools.

FAQ

Questions, answered

Why does docker pull fail even though I set http_proxy in my shell?

Because image pulls are done by the daemon, not your shell. Configure dockerd's proxy via a systemd drop-in (or Docker Desktop settings) with HTTP_PROXY/HTTPS_PROXY, then daemon-reload and restart docker.

How do I proxy docker build and docker run without editing the Dockerfile?

Add a proxies block to ~/.docker/config.json. The Docker client injects httpProxy/httpsProxy into every build step and container automatically, so no Dockerfile changes are needed.

How do I keep proxy credentials out of my image?

Don't set them with ENV in the Dockerfile — that bakes them into layers. Use build args or the client config.json injection so credentials stay on the build host, not in the shipped image.

Does s4m provide a registry mirror or cache?

No. s4m is a forward datacenter proxy for outbound traffic, not a pull-through registry cache. For caching images, run a registry mirror and route it through the proxy if needed.

Pull and build behind a proxy

Route the Docker daemon, builds and containers through the authenticated HTTP endpoint on proxy.s4m.online:3128. Metered pay-as-you-go, dedicated IP for CI allowlists.

People found this page by searching for

Real search phrases this page answers — the linked ones open the page that covers them in depth.