Skip to content

Docker Proxy Settings: Pulls, Containers, Builds and the proxyconnect Errors

Set Docker's proxy for pulls, containers and builds, and read each proxyconnect error from the words after it, with Go's source code and our own test run.

HProxy TeamAugust 24, 2026Updated October 23, 202611 min read

Docker Behind a Proxy: proxyconnect tcp, TLS Handshake Timeout, and x509 Errors Fixed

Docker has three proxy settings, and each one covers different traffic. The daemon's setting covers image pulls and pushes. The client's setting covers containers and builds. Docker Desktop has a third, and it ignores the daemon's file. Most proxy trouble comes from setting one of them and expecting another to follow.

The errors follow the same split. Every proxyconnect tcp error comes from the daemon on its way to a proxy, and the words after that label name the failure. We read Docker's documentation and source code, counted the errors on Docker's GitHub, and ran Go's HTTP library against a stub proxy on our own machine.

Three Docker proxy settings, three kinds of traffic
  1. Daemon (dockerd)

    pull, push, login: daemon.json or a systemd drop-in

  2. Containers and builds

    RUN steps and running apps: ~/.docker/config.json

  3. Docker Desktop

    its own Proxies settings; daemon.json proxies ignored

Source: Docker Docs: daemon proxy configuration, CLI proxy and Desktop settings, read October 22, 2026 · hproxy.comProxy.

Which Docker proxy setting does your command use?

What failsWhich settingWhere it lives
docker pull, docker push, docker loginThe daemondaemon.json or a systemd drop-in
A RUN step in docker buildThe build~/.docker/config.json or --build-arg
An app inside a running containerThe container~/.docker/config.json or --env
Anything on Docker Desktop for Windows or MacDesktopSettings, Resources, Proxies

Read the failing command first, then fix only that setting.

How do you set the daemon's proxy?

The daemon makes the registry connections for pulls and pushes. Docker's documentation recommends daemon.json:

{
  "proxies": {
    "http-proxy": "http://proxy.example.com:3128",
    "https-proxy": "http://proxy.example.com:3128",
    "no-proxy": "localhost,127.0.0.0/8,registry.internal.example.com,.corp"
  }
}

Then restart it with sudo systemctl restart docker. On a systemd host, a drop-in file works too, at /etc/systemd/system/docker.service.d/http-proxy.conf:

[Service]
Environment="HTTP_PROXY=http://proxy.example.com:3128"
Environment="HTTPS_PROXY=http://proxy.example.com:3128"
Environment="NO_PROXY=localhost,127.0.0.1,registry.internal.example.com,.corp"

A drop-in needs sudo systemctl daemon-reload before the restart, or the old values survive. Four details from the documentation save time:

  • The file wins. "Configuring the daemon directly takes precedence over environment variables."
  • Special characters need escaping. In the drop-in, characters such as #?!()[]{} "must be double escaped using %%".
  • Rootless Docker reads another place. Its drop-in sits in ~/.config/systemd/user/docker.service.d/, and systemctl runs with --user and without sudo.
  • The exclusion list has rules. example.com covers the domain and its subdomains, .example.com covers subdomains only, and a single * turns the proxy off for everything.

Check what the daemon believes with sudo systemctl show --property=Environment docker, and with docker info, which prints the proxy values in use.

How do containers and builds get a proxy?

Nothing inside a container inherits the daemon's proxy. A RUN apt-get update in a build, or pip install in a running container, opens its own connections. The client hands them a proxy from ~/.docker/config.json:

{
  "proxies": {
    "default": {
      "httpProxy": "http://proxy.example.com:3128",
      "httpsProxy": "http://proxy.example.com:3128",
      "noProxy": "localhost,127.0.0.1,.corp"
    }
  }
}

The file needs no restart. By Docker's documentation, it applies to new containers and builds only, and leaves running containers alone. For one run, pass the values by hand: --env HTTPS_PROXY=... on docker run and --build-arg HTTPS_PROXY=... on docker build.

Two warnings from the same documentation matter for anyone with a password in the proxy address:

  • Never use ENV for a build proxy. "Don't use the ENV Dockerfile instruction to specify proxy settings for builds. Use build arguments instead." ENV writes the proxy into the image, and every container from it inherits the address.
  • Container variables are readable. They "are stored as plain text in the container's configuration", so anyone with access to the Docker API can read them.

Compose files take the same variables under environment: or from an env_file:, and under build: args: for builds. Add your service names, such as db or redis, to NO_PROXY. Otherwise one container asks the proxy for another container it could reach directly.

How does Docker Desktop handle proxies?

Docker Desktop ignores the proxy keys in daemon.json, as the daemon documentation says plainly. It takes the proxy from Settings, Resources, Proxies, or from the system's own settings. Three points from Docker's Desktop documentation:

  • PAC files must cover registries. A PAC file has to return a proxy for Docker's registry addresses, "or image pulls will fail".
  • WPAD is not supported. Automatic discovery "is untested on both Windows and Mac", so enter a PAC address or a proxy by hand.
  • Containers have their own switch. They can follow the host, use the system proxy, use none, or take a manual setting.

What does proxyconnect tcp mean?

Docker's daemon reaches registries through Go's standard HTTP library, with the proxy taken from its environment. When a proxy is set, Go wraps every error on the way to it in one label. Its source says so in a line: return &net.OpError{Op: "proxyconnect", Net: "tcp", Err: err}.

So proxyconnect tcp tells you the failure happened between the daemon and the proxy, before any registry was involved. The words after it tell you what failed. We counted them in the issues on Docker's GitHub repositories:

What follows proxyconnect tcp in issues on Docker's GitHub
connection refused
41 issues
i/​o timeout
19 issues
no such host
14 issues
EOF
8 issues
first record does not look like a TLS handshake
3 issues
In 41 of the 93 issues, nothing was listening where the proxy should be.
Source: HProxy count through the public GitHub search API, October 22, 2026: 93 issues quoting proxyconnect tcp in moby/moby and six docker repositories; one issue can quote several · hproxy.comProxy.
  • connection refused: nothing listens at that address and port. The port is wrong, the proxy is down, or the host is on a network without it.
  • i/o timeout: no answer came back. Docker's registry client gives a connection 30 seconds, by its source code. A firewall or a wrong address usually explains the silence.
  • no such host: the daemon cannot resolve the proxy's own name. Fix DNS on the host, or use the proxy's address.
  • EOF: the proxy closed the connection without an answer. Check that the port speaks the protocol in the address, HTTP or SOCKS5.
  • first record does not look like a TLS handshake: the address says https:// and the proxy speaks plain HTTP. The next section shows it happen.

Two failures carry no proxyconnect label, because the connection to the proxy worked. A 407 Proxy Authentication Required means the proxy wants a login (11 issues on Docker's GitHub quote it). A bare Forbidden means the proxy refused the tunnel.

Why does an https:// proxy address break the pull?

In Go, the proxy address decides how the first hop starts. With http://, Go sends a plain CONNECT line to the proxy. With https://, Go opens a TLS handshake with the proxy itself before anything else.

We wrote a small Go program to watch both. Its stub "proxy" listens on 127.0.0.1, logs the first bytes it receives and refuses every request, so nothing leaves the machine. We ran it on Ubuntu 24.04 under WSL2:

Our own run on October 23, 2026: Go 1.24.2 net/http against a stub proxy on 127.0.0.1, Ubuntu 24.04 on WSL2. The stub forwards nothing, so no registry was contacted.

The bytes 16 03 01 open a TLS record of type handshake. A plain HTTP proxy cannot read them, so it answers with an HTTP error in plain text. Go reads that answer as its first TLS record and stops. The check in Go's TLS code is short: anything other than a handshake or an alert "does not look like a TLS handshake".

The fix is the scheme. Write http:// for a proxy that speaks plain HTTP, which is how the examples in Docker's documentation write the daemon's proxy. Keep https:// only for a proxy that really speaks TLS on that port. Docker Desktop, for one, recommends an https:// proxy address to protect passwords in transit, where the proxy supports it.

What do TLS handshake timeout and i/o timeout mean?

Both mean that no answer arrived in time. Docker's registry client waits 30 seconds for a connection and 10 seconds for a TLS handshake, by its source code.

Without the proxyconnect label, the daemon was talking to the registry directly. On a network that only lets traffic out through a proxy, that is the error you see before you set one. With the label, the proxy itself stalled. Test it from the host:

curl -v -x http://proxy.example.com:3128 https://registry-1.docker.io/v2/

A timeout is not a certificate problem. An untrusted certificate fails at once, with the x509 error below.

How do you fix x509: certificate signed by unknown authority?

Some corporate proxies inspect HTTPS. They sign each site's certificate with the organization's own certificate authority, which the Docker host does not trust. Install that authority on the host:

  • Debian and Ubuntu: copy the root certificate to /usr/local/share/ca-certificates/ and run sudo update-ca-certificates.
  • RHEL and Fedora: copy it to /etc/pki/ca-trust/source/anchors/ and run sudo update-ca-trust.
  • One registry only: Docker also reads ca.crt from /etc/docker/certs.d/<registry-hostname>/. On Linux, those roots are added to the system's own set.

Restart Docker afterwards. Inside a build, the container has its own trust store, so copy the certificate into the image and run the same update command there. Node.js and Python tools may also need NODE_EXTRA_CA_CERTS or REQUESTS_CA_BUNDLE pointed at it.

The wrong fix is an insecure registry in daemon.json. That turns verification off instead of trusting the right authority.

Can Docker pull images through a SOCKS5 proxy?

By Go's source, yes. The proxy code in Go's HTTP library handles socks5:// and socks5h:// addresses next to http:// and https://. A SOCKS5 address in the daemon's proxy settings therefore reaches that code. We read the code and did not run Docker with one, so test it on your own setup first. Our Linux proxy guide covers what other tools do with the same variables.

Where does a residential proxy fit?

At the container layer only. A scraper or checker in a container sometimes needs to reach sites from a home address, and the HTTP_PROXY variables carry that. Keep the login out of the image and pass it at run time from an env file:

# proxy.env: Lite gateway, HTTP port; use the login your dashboard shows
HTTPS_PROXY=http://YOUR-LOGIN:YOUR-PASSWORD@gw.hproxy.com:6969
HTTP_PROXY=http://YOUR-LOGIN:YOUR-PASSWORD@gw.hproxy.com:6969
NO_PROXY=localhost,127.0.0.1,db,redis

docker run --env-file proxy.env my-scraper

On Premium, premium.hproxy.com takes port 9000 for a rotating address and ports from 10000 up for sticky sessions. Our FlareSolverr guide covers a common trap here: inside a container, 127.0.0.1 is the container itself, not the host.

Keep image pulls off any per-GB proxy. Images are big:

What one pull of an official image weighs, compressed
python:3.12
411.7 MB
node:22
408.4 MB
ubuntu:24.04
29.8 MB
alpine:3.20
3.6 MB
One pull of python:3.12 moves about 0.4 GB.
Source: Docker Hub API, linux/amd64 compressed sizes, read October 23, 2026 · hproxy.comProxy.

A CI runner that starts empty pulls its base image on every run. A hundred fresh pulls of python:3.12 move about 41 GB. Through a proxy billed at $0.50/GB, that would cost $20.50 for traffic that gains nothing from a home address. Leave the daemon direct, or on your company's proxy, and let only the scraper's own requests use the gateway. Residential Lite costs $0.50/GB for one GB, falling to $0.44/GB at 2,000 GB+.

A checklist that ends most cases

  1. Name the setting. Pulls and pushes are the daemon, RUN steps are the build, apps are the container, Windows and Mac are Desktop.
  2. Print what it believes. Use systemctl show --property=Environment docker and docker info for the daemon, and docker run --rm alpine env | grep -i proxy for containers.
  3. Read the words after proxyconnect. Refused means the address or port, timeout means silence, no such host means DNS.
  4. Match the scheme to the proxy. Write http:// for a plain HTTP proxy.
  5. Reload, then restart. A changed drop-in needs daemon-reload before restart.
  6. Trust the authority. For x509, install the certificate and restart, and mark nothing insecure.

What this page could not check

  • We did not run Docker itself. Changing the daemon's proxy would have changed a working setup on our machine. The errors come from Go's HTTP library, which Docker's registry client uses, driven by our own program.
  • No real proxy was tested, corporate or commercial, ours included. The stub on 127.0.0.1 forwarded nothing.
  • The GitHub counts are text matches in issues on seven Docker repositories. One issue can match several errors, and most failures never reach GitHub.
  • Desktop is described from its documentation. We did not open its settings.
  • Releases change things. Docker's documentation, Go's wording and image sizes move with new versions; we will check this page again by January 23, 2027.

Sources

Frequently asked questions

How do I configure Docker to use a proxy?

Set it where the traffic starts. Image pulls and pushes come from the daemon, which reads the proxies block of daemon.json or a systemd drop-in, and needs a restart. Containers and builds take theirs from ~/.docker/config.json, or from --env and --build-arg. Docker Desktop ignores the daemon.json proxies and uses its own Proxies settings.

What does proxyconnect tcp mean in a Docker error?

The daemon failed while connecting to the proxy it was told to use. Go's HTTP library adds that label to any error on the way to a proxy, and the words after it say what went wrong. In 41 of 93 issues on Docker's GitHub that quote it, the next words were connection refused.

Why does Docker say tls: first record does not look like a TLS handshake?

The proxy address starts with https://, so Go opened a TLS handshake with the proxy itself. A proxy that speaks plain HTTP answers in plain text, and Go rejects that first answer. Change the address to http:// unless the proxy really speaks TLS on that port.

How do I fix x509: certificate signed by unknown authority behind a proxy?

The proxy inspects HTTPS and signs it with your organization's own certificate authority. Install that root certificate in the host's trust store, or as ca.crt under /etc/docker/certs.d/ for one registry, and restart Docker. Marking the registry insecure only turns the check off.

Can Docker pull images through a SOCKS5 proxy?

By Go's source, yes. The HTTP library the daemon uses for registries accepts socks5:// and socks5h:// proxy addresses, so one can go in the daemon's proxy settings. We did not run Docker with one.

Why can my containers not reach the internet when docker pull works?

The daemon's proxy covers pulls only. Processes inside a container or a build need HTTP_PROXY and HTTPS_PROXY of their own, from ~/.docker/config.json or from --env and --build-arg.

Get proxies that are alive right now

Our free proxy list re-checks every exit every few minutes across 100+ countries, with a live last-checked time, so you copy IPs that worked moments ago, not a stale text dump. When the location has to survive a real check, the paid network holds up.

129M+ proxy checks run · 100+ countries · HTTP / HTTPS / SOCKS · re-checked every few minutes · no signup

HProxy.

Honest guides and comparisons on proxies, scraping and staying unblocked, from the team that runs the network.

RSS feed