Repository navigation
[macOS] Built-in HTTP proxy leaks one backend socket per plain-HTTP container request (upstream stays CLOSE_WAIT/CLOSED); backend crashes with "too many open files" after ~92k requests #752
Description
Bug report
Summary
When macOS has a system HTTP proxy configured, Docker Desktop forwards plain-HTTP requests from containers through its built-in HTTP proxy to that upstream proxy (httpproxy.log: container connecting via static system HTTP proxy). For every such request, the com.docker.backend services process keeps the upstream socket (backend → system proxy) open forever: the TCP connection goes CLOSE_WAIT and then CLOSED, but the file descriptor is never closed. The request itself completes normally (successful after … (0 remaining in-progress HTTP requests)).
HTTPS traffic does not leak, neither through transparent :443 forwarding nor through CONNECT tunnels.
Over time the leaked descriptors exhaust the process limit. Once the backend reaches kern.maxfilesperproc (92160 on this machine), accept() on stats.sock fails with EMFILE, the "VM stats server" stops, the backend cancels all services, and every container on the host goes down.
Minimal reproduction
Preconditions:
- A local HTTP proxy set as the macOS system proxy. We use mihomo (Clash) at
127.0.0.1:7897. We have not tried other proxies. - Docker Desktop proxy settings left at the default (use the system proxy).
Steps:
# 1. The backend process that owns the forwarded connections
P=$(pgrep -f 'com.docker.backend services')
# 2. Count sockets from the backend to the upstream proxy (adjust the port to your proxy)
lsof -n -P -p "$P" | grep -- '->127.0.0.1:7897' | awk '{print $NF}' | sort | uniq -c
# 3. 100 plain-HTTP requests from a throwaway container
docker run --rm alpine sh -c 'for i in $(seq 100); do wget -q -O /dev/null http://example.com/; done'
# 4. Count again: +100, all CLOSE_WAIT, later CLOSED, never released
lsof -n -P -p "$P" | grep -- '->127.0.0.1:7897' | awk '{print $NF}' | sort | uniq -c
# 5. Control: 100 HTTPS requests, +0
docker run --rm alpine sh -c 'for i in $(seq 100); do wget -q -O /dev/null https://example.com/; done'
lsof -n -P -p "$P" | grep -- '->127.0.0.1:7897' | awk '{print $NF}' | sort | uniq -cObserved on 2026-10-16 (backend restarted ~8 h earlier):
| Step | Sockets to upstream proxy | Total fds of services |
|---|---|---|
| Before | 0 | 287 |
After 100 × http://example.com/ (100/100 HTTP 200, no redirect) |
104: 100 CLOSE_WAIT + 4 ESTABLISHED |
413 |
After 100 × https://example.com/ (100/100 OK) |
100: all 100 now CLOSED |
428 |
httpproxy.log for the same window:
- 100 ×
HTTP GET http://example.com/: container connecting via static system HTTP proxy, each withsuccessful after …; - 100 ×
TCP …:443 TLS ServerName example.com container connecting via static system HTTPS proxy; - 12 host
CONNECTs from Docker Desktop itself; - no other container traffic.
So every plain-HTTP request leaked exactly one descriptor, and the HTTPS requests leaked none.
Production impact
One container in our setup sends about 5,400 plain-HTTP requests every night.
- One night of production data. The proxy's request counter went up by 5,387, and
CLOSEDsockets to the proxy went up by 5,384. The httpproxy log shows 5,383 containerPOST http://…requests in that window. None of these sockets were released over the following 11.5 hours. - The crash. On 2026-10-14, the backend crashed 16.9 days after its last start. That is 16 full nights plus part of the 17th, which matches 92160 ÷ ~5,400. Two earlier host-wide outages happened at 19.0 and 21.9 days of uptime, but their logs had already rotated away.
Excerpt from com.docker.backend.log at the crash. Paths are as logged by Docker Desktop:
[2026-10-14T13:38:44.873Z][com.docker.backend.ipc][W] … GET /ping … dial unix <HOME>/Library/Containers/com.docker.docker/Data/stats.sock: socket: too many open files
[2026-10-14T13:38:45.877Z][com.docker.backend.services] backend service "VM stats server" stopped with error: VM stats server: accepting vm proxy service connection: accept unix <HOME>/Library/Containers/com.docker.docker/Data/stats.sock: accept: too many open files
[2026-10-14T13:38:53.173Z][com.docker.backend] backend crashed, dumping error to file and reporting to user: running services: running services: running VM stats server: …: accept: too many open files
At startup the backend logs maximum number of file descriptors is 9223372036854775807. The effective soft limit appears to be kern.maxfilesperproc (92160), which matches the crash point.
Expected behavior
After a forwarded plain-HTTP request completes, the upstream connection should be closed, or reused through a bounded keep-alive pool, and its descriptor released. The number of open descriptors should not grow with the number of requests served.
Workarounds we use
- Prefer HTTPS from containers. HTTPS goes through CONNECT or transparent :443 forwarding, which does not leak.
- Restart Docker Desktop before the backend reaches the limit. We now monitor the
servicesprocess's descriptor count.
Notes
- Observed on 4.79.0. The release notes for 4.80.0 through 4.95.0 do not mention a related fix, but we have not yet re-tested on the latest version. We will update this issue when we do.
- Networking stack: gVisor (
NetworkType=gvisor). docker inforeportsHTTP Proxy: http.docker.internal:3128andHTTPS Proxy: http.docker.internal:3128.- Happy to provide more data, or a diagnostics upload if needed.
Platform
macOS
Version information
macOS 26.6.2 (25G83), Apple M4, 24 GB RAM
Docker Desktop 4.79.0 (230596)
Docker Engine 29.5.3 (API 1.54), linux/arm64, kernel 6.12.76-linuxkit
Networking: gVisor; proxy: system proxy (static), upstream mihomo/Clash HTTP proxy on 127.0.0.1:7897Diagnostics ID
No response
Activity
bsousaa commentedon Oct 21, 2026
Can you please share a diagnostics id?