Skip to content

[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

Open

Description

@sky-silence

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 -c

Observed 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 with successful 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 CLOSED sockets to the proxy went up by 5,384. The httpproxy log shows 5,383 container POST 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 services process'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 info reports HTTP Proxy: http.docker.internal:3128 and HTTPS 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:7897

Diagnostics ID

No response

Activity

  1. added theissue type on Oct 17, 2026
  2. bsousaa commented on Oct 21, 2026

    @bsousaa
    Collaborator

    Can you please share a diagnostics id?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions