KhueApps
Home/DevOps/Fix Docker pull failures behind proxies with HTTP(S)_PROXY/NO_PROXY

Fix Docker pull failures behind proxies with HTTP(S)_PROXY/NO_PROXY

Last updated: October 07, 2025

Overview

If docker pull fails behind a corporate proxy, it’s almost always the Docker daemon (dockerd) lacking correct proxy settings or trust for your proxy’s TLS certificate. The client binary (docker) talks to dockerd over a local socket; the daemon performs the registry requests. Configure proxy variables for the daemon, set NO_PROXY correctly, and install your proxy’s CA if it intercepts TLS.

Common errors and likely causes:

Error snippetLikely cause
x509: certificate signed by unknown authorityMissing corporate proxy/registry CA
proxyconnect tcp: dial tcp ...: i/o timeoutWrong proxy URL or unreachable proxy
unexpected status code 407/403Auth required/wrong credentials for proxy
no such host / TLS handshake timeoutNO_PROXY or DNS misconfig

Quickstart (Linux with systemd)

Goal: make dockerd use the proxy, skip it for internal hosts, and trust the corporate CA.

  1. Create a systemd drop-in for Docker’s proxy.
  2. Set HTTP_PROXY, HTTPS_PROXY, and a precise NO_PROXY.
  3. Reload, restart, verify.

Minimal commands:

# 1) Create drop-in directory
sudo mkdir -p /etc/systemd/system/docker.service.d

# 2) Configure proxy env for the Docker daemon
sudo tee /etc/systemd/system/docker.service.d/http-proxy.conf >/dev/null <<'EOF'
[Service]
Environment="HTTP_PROXY=http://proxy.corp.example.com:3128"
Environment="HTTPS_PROXY=http://proxy.corp.example.com:3128"
Environment="NO_PROXY=localhost,127.0.0.1,::1,registry-1.docker.io,auth.docker.io,.docker.io,.docker.com,registry.internal.example.com,10.0.0.0/8,192.168.0.0/16"
EOF

# 3) Trust the corporate proxy/registry CA (Debian/Ubuntu)
sudo install -m 0644 corp-proxy-ca.crt /usr/local/share/ca-certificates/corp-proxy-ca.crt
sudo update-ca-certificates
# RHEL/CentOS/AlmaLinux: place cert in /etc/pki/ca-trust/source/anchors/ then run:
# sudo update-ca-trust

# 4) Apply and restart Docker
sudo systemctl daemon-reload
sudo systemctl restart docker

# 5) Verify the daemon env and pull
systemctl show docker -p Environment
sudo docker pull busybox:latest

If your proxy requires auth, encode credentials in the URL safely, for example:

export HTTPS_PROXY="http://user:[email protected]:3128"

Percent-encode special characters in user/password.

Minimal Working Example (MWE)

A self-contained working configuration for a host behind proxy.corp.example.com:3128 with an internal registry registry.internal.example.com, skipping the proxy for Docker Hub and internal networks.

# /etc/systemd/system/docker.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=http://proxy.corp.example.com:3128"
Environment="HTTPS_PROXY=http://proxy.corp.example.com:3128"
Environment="NO_PROXY=localhost,127.0.0.1,::1,registry-1.docker.io,auth.docker.io,.docker.io,.docker.com,registry.internal.example.com,10.0.0.0/8,192.168.0.0/16"

Apply with:

sudo systemctl daemon-reload && sudo systemctl restart docker
sudo docker pull alpine:3.20

Notes:

  • Use uppercase variable names for systemd.
  • NO_PROXY accepts comma-separated hosts, literal IPs, and domain suffixes (prefix with a dot). Some stacks do not honor CIDR ranges; prefer explicit hosts/IPs if in doubt.

Step-by-step diagnosis and fix

  1. Confirm the daemon sees proxy variables
  • Check drop-ins: ls -l /etc/systemd/system/docker.service.d/
  • Print env: systemctl show docker -p Environment
  • If empty/missing, your drop-in file path or syntax is wrong.
  1. Verify proxy reachability and credentials
  • From the host: curl -x http://proxy.corp.example.com:3128 https://registry-1.docker.io/v2/ -I
  • Expect 200/401/403. 407 means proxy auth required; configure credentials.
  1. Tune NO_PROXY
  • Always include localhost, 127.0.0.1, ::1.
  • Add internal registries (e.g., registry.internal.example.com) and any endpoints that your proxy breaks.
  • To bypass for all Docker Hub subdomains, include .docker.io and .docker.com.
  1. Install trust for corporate CA
  • System trust: place corp-proxy-ca.crt into the OS trust store and run update-ca-certificates (Debian/Ubuntu) or update-ca-trust (RHEL family).
  • Private registry trust: for a private hostname, you can also place the CA at /etc/docker/certs.d/registry.internal.example.com/ca.crt to scope trust to that registry.
  1. Restart Docker and retest
  • sudo systemctl restart docker
  • sudo docker pull busybox:latest
  1. Keep the client environment clean
  • Unset client-level proxy for Docker commands to avoid confusion: unset HTTP_PROXY HTTPS_PROXY NO_PROXY
  • The daemon performs the pull; client proxies only affect commands that directly call the network (e.g., docker login).

Pitfalls and gotchas

  • Wrong place: Editing /etc/docker/daemon.json proxies affects container runtime egress, not the daemon’s own pulls. Use systemd env for dockerd.
  • Typos and casing: HTTP_PROXY/HTTPS_PROXY/NO_PROXY must be uppercase for systemd. Commas only; no spaces.
  • Missing daemon reload: Always run systemctl daemon-reload after changing drop-ins.
  • Proxy auth characters: If your password has @ or :, percent-encode them in the proxy URL.
  • TLS interception without trust: MITM proxies require installing their CA, or pulls will fail with x509 errors.
  • Rootless Docker: Configure the user-level systemd service (systemctl --user edit docker) or set env before starting dockerd-rootless; path differs from this guide.
  • Docker Desktop: Use the UI to set proxies; this article targets Linux servers.

Performance notes

  • Use a local caching registry or proxy (Artifactory, Nexus, Harbor) to accelerate repeated pulls. Add it as a mirror:
{
  "registry-mirrors": ["https://registry.internal.example.com"]
}

Place this in /etc/docker/daemon.json and restart Docker.

  • Keep NO_PROXY minimal. Overly broad NO_PROXY routes too much traffic direct, hurting reliability.
  • Prefer HTTPS proxies. Even when specified as http:// in the URL, HTTPS_PROXY tunnels TLS via CONNECT to preserve end-to-end encryption when not intercepted.

FAQ

  • Does the Docker daemon actually use HTTP_PROXY/HTTPS_PROXY? Yes. Dockerd inherits environment variables from systemd. That’s why the systemd drop-in is required.

  • Should I set proxy variables in /etc/profile or the shell? You can for general tools, but Docker pulls depend on dockerd’s environment, not your shell. Configure systemd.

  • How do I set proxies for containers’ outbound traffic? Use the proxies section in /etc/docker/daemon.json or set env vars per container. This is separate from daemon proxy.

  • What about containerd-only hosts? Configure proxy env on the containerd service (e.g., /etc/systemd/system/containerd.service.d/http-proxy.conf) similarly, then restart containerd.

  • How do I bypass the proxy for a single pull? Temporarily run with a clean environment so the client doesn’t interfere and rely on daemon config: env -u HTTP_PROXY -u HTTPS_PROXY -u NO_PROXY sudo docker pull alpine

Series: Docker

DevOps