# net/http: TLS handshake timeout：Docker 拉镜像超时的修法

> docker pull 报 net/http: TLS handshake timeout，说明和镜像仓库的 TLS 握手超过了 10 秒。要修的是守护进程代理、网络链路、MTU 或镜像加速地址。

Source: https://ssl.sh/zh/errors/docker-tls-handshake-timeout/
Last updated: 2026-11-10

**`net/http: TLS handshake timeout` 的意思是：Docker 已经通过 TCP 连上了镜像仓库，但 [TLS 握手](https://ssl.sh/zh/glossary/tls-handshake/)在 10 秒内没有完成。** 这是网络问题，不是证书问题。常见原因是到仓库的链路慢或丢包、Docker 守护进程没有使用你终端里的代理、MTU 问题导致大包被丢弃，或者配置的镜像加速地址已经不响应了。

## 这条报错是什么意思

这条消息来自 Go 的 `net/http` 包，Docker、containerd、kubectl、Helm 都基于它。Go 的 `http.Transport` 在握手时间超过 `TLSHandshakeTimeout` 时放弃；Docker 守护进程访问仓库的 Transport 把它设为 10 秒（TCP 连接超时为 30 秒）。通常你会看到：

```text
Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: TLS handshake timeout
```

`docker pull` 是让**守护进程**去下载镜像。守护进程从自己的环境变量读取代理（代码里是 `http.ProxyFromEnvironment`），你在终端里 export 的变量对它不起作用。

## 常见原因（按出现频率排序）

最常见的原因是到仓库的链路慢、拥塞或丢包，其次是守护进程需要代理却没配置。

### 1. 到仓库的链路慢、拥塞或丢包

跨境链路、拥挤的 Wi-Fi、丢包严重的网络，都会让握手需要的几个来回超过 10 秒。在国内直接访问 Docker Hub 时这种情况尤其常见。

- **确认：** 在同一台机器上测量握手耗时：

```bash
curl -sS -o /dev/null -w 'connect=%{time_connect}s tls=%{time_appconnect}s http=%{http_code}\n' https://registry-1.docker.io/v2/
```

正常结果是 HTTP 401（需要认证），`tls` 远小于 1 秒。如果要好几秒甚至卡住，说明在 Docker 之外也能复现。

- **修复：** 使用就近的镜像加速或拉取缓存，或者把构建放到链路更好的网络里。

### 2. 守护进程需要代理却没配置

公司网络和部分云网络只允许通过代理访问外网 HTTPS。终端里设置了 `HTTPS_PROXY`，守护进程却没有。

- **确认：**

```bash
docker info | grep -iE "http proxy|https proxy|no proxy"
sudo systemctl show --property=Environment docker
```

- **修复：** 给守护进程配置代理（命令见下文）。如果代理会解密 HTTPS，随后的报错会变成 [x509: certificate signed by unknown authority](https://ssl.sh/zh/errors/docker-x509-certificate-signed-by-unknown-authority/)，需要为 Docker 安装代理的根证书。

### 3. MTU 黑洞

VPN、PPPoE 拨号和部分云网络的路径 MTU 小于 1500，同时“需要分片”的 ICMP 消息又被拦截。小的 ClientHello 能过去，服务器发来的较大证书数据包过不来，握手就卡住了。

- **确认：** 用 `ip link show` 查看网卡 MTU，再执行 `ping -M do -s 1472 registry-1.docker.io`（Linux）。出现 “Message too long” 或没有响应、而更小的包能通，就是 MTU 问题。
- **修复：** 把网卡 MTU 设成链路实际支持的值，或者在 VPN、路由器上开启 MSS 钳制。如果是容器内构建受影响，在 `daemon.json` 里设置默认网桥的 `"mtu"`。

### 4. 配置的镜像加速地址变慢或失效

`registry-mirrors` 里填的地址不再响应，就会出现针对这个加速地址的超时。

- **确认：** `docker info | grep -A3 "Registry Mirrors"`，再用上面的 curl 命令逐个测试加速地址。
- **修复：** 删掉失效的地址，重启守护进程。

### 5. 防火墙或检测设备拖住了 TLS

有些设备会把 TLS 会话扣下来检查，或者对特定目标限速。如果 curl 测试在别的网络很快、只在这里慢，请网络管理员排查。

## 分平台修复命令

各平台的核心修法是给守护进程本身配代理，并重启 Docker 让配置生效。

**Linux，通过 daemon.json 配置守护进程代理**（Docker 推荐的方式）：

```json
{
  "proxies": {
    "http-proxy": "http://proxy.example.com:3128",
    "https-proxy": "http://proxy.example.com:3128",
    "no-proxy": "localhost,127.0.0.1,.example.com"
  }
}
```

```bash
sudo systemctl restart docker
```

**Linux，systemd drop-in**（`/etc/systemd/system/docker.service.d/http-proxy.conf`）：

```ini
[Service]
Environment="HTTPS_PROXY=http://proxy.example.com:3128"
Environment="NO_PROXY=localhost,127.0.0.1"
```

```bash
sudo systemctl daemon-reload && sudo systemctl restart docker
```

**Docker Hub 镜像加速**（`/etc/docker/daemon.json`）：

```json
{ "registry-mirrors": ["https://mirror.example.com"] }
```

国内服务器可以填云厂商控制台里给出的镜像加速地址（如果该厂商提供），或者公司内部自建的拉取缓存。加速地址只对 Docker Hub 镜像生效，改完要重启守护进程，并用 `docker info` 确认已加载。公共加速服务随时可能调整或停止，生产环境更稳妥的做法是把常用镜像同步到自己的私有仓库（比如云厂商的容器镜像服务）。

**Docker Desktop（Windows 和 macOS）：** 设置 → Resources → Proxies（可以跟随系统代理）；设置 → Docker Engine 里编辑 `daemon.json`，填写镜像加速地址。

**使用 containerd 的 Kubernetes 节点：** kubelet 事件里也会出现同样的 Go 报错，代理要配置在 containerd 服务的环境里，而不是 Docker 的。

## 不推荐：关闭证书校验

把仓库加进 `insecure-registries`，或者为了“提速”在加速器、代理上关闭 TLS 校验，都修不好超时，反而去掉了镜像完整性的保护。通过未经校验的连接拉取镜像，链路上的任何人都可以把它换成恶意镜像。

更安全的替代办法：配好守护进程代理；使用可信的 HTTPS 镜像加速；修正 MTU；如果代理会重新签名 TLS，就把它的根证书放进 `/etc/docker/certs.d/<仓库地址>/ca.crt` 或系统信任库。

## 怎么确认已经修好

守护进程信息里能看到你配置的代理和加速地址，`docker pull` 不再超时，就说明修好了：

```bash
docker info | grep -iE "proxy|mirrors" -A1
time docker pull hello-world
```

`docker build` 里的 `RUN apt-get`、`RUN pip install` 是在构建容器里执行的，走的是构建容器的网络和代理设置，和守护进程拉镜像不是一回事，需要通过构建参数单独传入代理。如果只是偶发超时，先重试一两次再下结论；如果每次都超时，再按上面的顺序逐项排查。改完守护进程配置要重启 Docker，否则新设置不会生效。

再跑一次上面的 curl 计时命令，TLS 耗时在一秒以内说明链路正常。握手的过程以及大数据包出现在哪一步，见 [TLS 握手过程](https://ssl.sh/zh/learn/tls-handshake/)。
