docker buildx: the builder had a proxy, the client didn't
My image build script started failing on its very first line, FROM python:3.10,
with failed to fetch anonymous token ... TLS handshake timeout. The odd part:
the buildx builder I use is created with a proxy for exactly this reason, and
from another shell the same build worked.
The token request is not made by the builder. It is made by the docker buildx
client in your own shell, using your shell’s proxy variables. My interactive
zsh exports none, so that one request went direct and timed out. Two export
lines in the build script fixed it.
The setup
- Ubuntu, kernel 5.15, BuildKit v0.22.0 in a
docker-containerbuilder namedproxy-builder - The builder was created with
--driver-opt env.HTTPS_PROXY=..., sobuildkitdinside its container goes through a proxy run.shbuildswilliam-ubuntu-python3:v1.0withdocker buildx build --builder proxy-builder --loadand pushes it to a LAN registry at192.168.1.162:5000- Docker Hub is not reachable directly from this network
The error
|
|
The step dies after about 10 seconds, which matches Go’s default
TLSHandshakeTimeout. The TCP connection opens, but the TLS handshake never
finishes. That’s what a blocked direct connection looks like here.
Step 1: the builder’s proxy works
The builder’s proxy is visible in docker buildx inspect:
|
|
|
|
Both proxies I have reached the exact URL from the error in about a second:
|
|
|
|
The buildkitd log in the builder container showed the same error, so I
assumed the request came from there:
|
|
|
|
Wrong turn: “it was a blip”
I ran a minimal FROM python:3.10 build through proxy-builder, it passed,
and I put the failure down to a flaky SOCKS proxy. I also re-created the
builder to use the faster HTTP proxy at 192.168.1.82:20170. The full
run.sh then built and pushed fine.
Then I ran bash ./run.sh in my normal terminal and got the exact same error.
What I had missed: the shell I tested from was not my normal shell. It
already had HTTPS_PROXY exported, and my interactive zsh doesn’t. So
every test I ran passed for a reason that didn’t apply to my real terminal.
Re-creating the builder did no harm, but it didn’t fix anything.
Step 2: reproduce it by removing the client’s proxy
Same builder and same Dockerfile. The only change is whether the
docker buildx process itself sees the proxy variables:
|
|
|
|
The first command fails and the second one passes. And a clean interactive zsh confirms what my terminal actually exports:
|
|
|
|
Only GOPROXY. My .zshrc sets proxies inside a few aliases and functions
(proxychains4 -q, env HTTPS_PROXY=... some-app) but never exports one for
the whole shell.
Why the client fetches the token
With BuildKit, registry credentials stay on the client. When the builder needs
to pull from a registry, it asks the client over the build session, and the
client’s auth provider fetches the bearer token from auth.docker.io, anonymous
or not. The builder then uses that token to pull the layers through its own
proxy.
So a pull from Docker Hub needs two working network paths:
| request | made by | proxy it uses |
|---|---|---|
GET auth.docker.io/token |
docker buildx client (your shell) |
your shell’s HTTPS_PROXY |
manifests and layers from registry-1.docker.io |
buildkitd in the builder container |
the builder’s env.HTTPS_PROXY |
The error is reported by the builder, which is why I first looked there. But
it is relaying a failure from the client side. Setting
--driver-opt env.HTTPS_PROXY only covers the second row.
The fix
Export the proxy in the build script, so it doesn’t depend on whichever shell runs it.
run.sh:
|
|
To check it, run the script from a clean zsh with no proxy set, the same way it runs from my terminal:
|
|
|
|
A few details that matter:
-
The scheme has to match what the port speaks. My SOCKS port only speaks SOCKS5. Pointing an
http://proxy at it fails right away:1curl -sS -o /dev/null -m 15 -x http://<socks-host>:1080 https://auth.docker.io/token1curl: (56) Proxy CONNECT abortedUse
socks5://<socks-host>:1080for that one. The buildx client is a Go program and acceptssocks5://inHTTPS_PROXY; I tested it and the token fetch went through. The192.168.1.82:20170port speaks HTTP, sohttp://is correct there. -
NO_PROXYkeeps LAN traffic direct. The push goes to192.168.1.162:5000, and you don’t want local addresses going through the proxy. -
Put it in the script, not in
.zshrc. Exporting a proxy for the whole shell changes every other program you run. The script is the one place that needs it.
Takeaways
- With BuildKit, a Docker Hub pull crosses two processes, and both need a network path. The builder proxy covers layer downloads; the client’s environment covers the auth token.
- The process that reports an error is not always the one that failed.
buildkitdlogged the timeout, but the request that timed out came from the client. - Test from the same environment the failure happens in. My checks passed
in a shell that already had a proxy exported. A clean
env -i ... zsh -ireproduced the failure on the first try.
Related: docker buildx,
docker 配置在pull阶段使用 proxy
(that one is the dockerd side, for plain docker pull).
References
- Configure BuildKit: proxy and registry settings
- Docker build: use a proxy
- BuildKit client-side auth provider:
session/auth/authprovider/authprovider.go
相关内容
- docker 使用 host 代理
- docker buildx
- Installing Clash Verge Rev on Ubuntu 20.04 / Mint 20 — and upgrading its mihomo core
- Installing ChatGPT Desktop on Mint 20 / Ubuntu 20.04 — when the blocker isn't glibc
- Installing Claude Desktop on Ubuntu 20.04 / Mint 20 (glibc 2.31)
支付宝
微信

william