Description
Summary
Calling docker.from_env()
in a rootless Docker environment (UNIX socket: /run/user/1001/docker.sock
) causes the number of open file descriptors (FDs) held by dockerd
to continuously increase, even when the client is immediately discarded and the script terminates.
This FD leak is not observed in rootful Docker environments or when using the Docker CLI via subprocess
.
Steps to Reproduce
- Run a clean rootless test container:
docker run -it --rm \
--tmpfs /run \
-v /run/user/1001/docker.sock:/run/user/1001/docker.sock \
-e DOCKER_HOST=unix:///run/user/1001/docker.sock \
python:3.12-slim /bin/bash
- Inside the container:
pip install docker
echo "import docker; docker.from_env()" > test_fd.py
-
Run the script multiple times:
for i in $(seq 1 100); do python test_fd.py; done
-
Check FD count on host:
lsof -w | grep dockerd | wc -l
The count increases with every run. Even when using .close()
, the leak persists.
What We Tried
- Confirmed
.close()
has no effect - Tested both:
-
`docker==5.0.3` with legacy stack (`urllib3==1.26.15`)
-
`docker==7.1.0` with `requests-unixsocket` and `urllib3==2.2.1`
-
- Verified it does not leak in rootful Docker
- Verified subprocess-based usage (
docker ps
) does not leak - Upgraded containerd from v1.7.1 → v1.7.27: still leaks
Environment
Docker Info
Client:
Version: 24.0.4
Context: default
Debug Mode: false
Server:
Containers: 3
Running: 1
Paused: 0
Stopped: 2
Images: 44
Server Version: 24.0.4
Storage Driver: fuse-overlayfs
Logging Driver: json-file
Cgroup Driver: none
Cgroup Version: 1
Runtimes: io.containerd.runc.v2 runc
containerd version: 1677a17964311325ed1c31e2c0a3589ce6d5c30d
runc version: v1.1.7-0-g860f061
Kernel Version: 4.18.0-372.9.1.el8.x86_64
Operating System: Red Hat Enterprise Linux 8.6 (Ootpa)
Docker Root Dir: /bb/cc/docker_data
rootless: true
OS info
NAME="Red Hat Enterprise Linux"
VERSION="8.6 (Ootpa)"
ID="rhel"
VERSION_ID="8.6"
PLATFORM_ID="platform:el8"
PRETTY_NAME="Red Hat Enterprise Linux 8.6 (Ootpa)"
containerd version
containerd github.com/containerd/containerd v1.7.1 1677a17964311325ed1c31e2c0a3589ce6d5c30d
Python Environment
docker: 5.0.3 and 7.1.0 (tested both)
requests: 2.26.0 → 2.31.0
urllib3: 1.26.15 and 2.2.1
requests-unixsocket: 0.3.0 (used in 7.x only)
Python: 3.12.1
Rootless Docker Setup
- Installed using Docker’s official script:
dockerd-rootless-setuptool.sh install
- Managed via:
systemctl --user start docker
- Docker socket path:
unix:///run/user/1001/docker.sock
dockerd
process runs under non-root user (UID 1001
)- Verified environment variable:
DOCKER_HOST=unix:///run/user/1001/docker.sock
rootlesskit
version: 1.1.0
Workaround
Using the Docker CLI via subprocess avoids the leak completely:
import subprocess
subprocess.run(["docker", "ps"])
Note:
To test this workaround inside the container, you must install the Docker CLI first:
apt update && apt install -y docker.io
Then you can validate with:
for i in $(seq 1 100); do python3 -c 'import subprocess; subprocess.run(["docker", "ps"])'; done
- Can you confirm this as a bug in the SDK or socket/session cleanup under rootless Docker?
- Could this be an issue with
requests-unixsocket
or how SDK wraps it? - Any known workaround or future fix?
Update: Tried Upgrading RootlessKit (v2.3.5)
We also upgraded RootlessKit to version 2.3.5 using the official release:
https://github.com/rootless-containers/rootlesskit/releases/download/v2.3.5/rootlesskit-x86_64.tar.gz
Confirmed it's in use via:
systemctl --user status docker
Output:
Loaded: loaded (/home/cc/.config/systemd/user/docker.service; enabled; vendor preset: enabled)
Active: active (running) since Fri 2025-05-23 14:08:13 UTC; 21s ago
Docs: https://docs.docker.com/go/rootless/
Main PID: 37479 (rootlesskit)
CGroup: /user.slice/user-1001.slice/[email protected]/docker.service
├─37479 rootlesskit --net=vpnkit ...
├─37487 /proc/self/exe ...
├─37500 vpnkit --ethernet ...
├─37513 dockerd
└─37543 containerd ...
Also verified version directly:
/bb/cc/docker_bin/rootlesskit --version
-> rootlesskit version 2.3.5
Despite the upgrade and confirmed restart of the rootless Docker daemon, the FD leak still persists when calling docker.from_env()
repeatedly.