This error occurs when the Docker CLI cannot establish a TCP connection to the Docker daemon. The daemon may not be running, not listening on the expected port, or network/firewall issues are blocking the connection.
The "dial tcp: connect: connection refused" error indicates that your Docker client is trying to connect to a Docker daemon via TCP (typically on port 2375 or 2376), but the connection is being actively refused. This is different from a timeout - a "connection refused" means something is listening on the network stack but explicitly rejecting the connection, or nothing is bound to that port at all. This error commonly appears in several scenarios: - **CI/CD pipelines**: When using Docker-in-Docker (dind) services in GitLab CI, GitHub Actions, or Jenkins, where the Docker daemon runs as a separate service container - **Remote Docker hosts**: When the DOCKER_HOST environment variable points to a remote machine or container - **WSL environments**: When Windows Subsystem for Linux is configured to connect to Docker Desktop via TCP - **Development tools**: IDEs and build tools that need to communicate with Docker over TCP The URL in the error message (e.g., `http://docker:2375`) reveals important information: the hostname being contacted and the port being used. Port 2375 is the default unencrypted Docker API port, while port 2376 is used for TLS-encrypted connections.
First, check if Docker is actually running on the target system:
On Linux:
sudo systemctl status dockerOn Windows (PowerShell):
Get-Service *docker*On macOS with Docker Desktop:
Check if the Docker icon appears in the menu bar and shows "Docker Desktop is running".
If Docker is not running, start it:
# Linux
sudo systemctl start docker
# Windows/macOS
# Open Docker Desktop applicationThe DOCKER_HOST variable tells the Docker CLI where to connect. Verify its value:
echo $DOCKER_HOSTCommon values and what they mean:
- Empty or unset: Uses default Unix socket /var/run/docker.sock
- `tcp://localhost:2375`: Connects to local daemon via TCP (no TLS)
- `tcp://docker:2375`: Connects to a host named "docker" (common in CI/CD)
- `tcp://localhost:2376`: Connects with TLS encryption
If DOCKER_HOST points to an incorrect location, unset it:
unset DOCKER_HOST
docker versionIf that works, remove DOCKER_HOST from your shell configuration files (~/.bashrc, ~/.zshrc).
In GitLab CI with Docker-in-Docker, the Docker daemon runs in a separate service container with hostname docker, not localhost:
# .gitlab-ci.yml
image: docker:latest
services:
- docker:dind
variables:
# CORRECT: Use 'docker' hostname for the dind service
DOCKER_HOST: tcp://docker:2375
# For TLS-enabled Docker 19.03+:
# DOCKER_HOST: tcp://docker:2376
# DOCKER_TLS_CERTDIR: "/certs"
# DOCKER_CERT_PATH: "/certs/client"
# DOCKER_TLS_VERIFY: 1
build:
script:
- docker version
- docker build -t myimage .Common mistake: Using tcp://localhost:2375 will fail because localhost refers to the job container, not the dind service container.
In GitHub Actions with a Docker service container:
jobs:
build:
runs-on: ubuntu-latest
services:
dind:
image: docker:dind
options: --privileged
ports:
- 2375:2375
env:
DOCKER_TLS_CERTDIR: ""
steps:
- uses: actions/checkout@v4
- name: Wait for Docker daemon
run: |
timeout 30 sh -c 'until docker info; do sleep 1; done'
env:
DOCKER_HOST: tcp://localhost:2375
- name: Build image
run: docker build -t myimage .
env:
DOCKER_HOST: tcp://localhost:2375Note: GitHub Actions' default runner already has Docker installed. You only need dind for special cases like testing Docker itself.
The Docker daemon inside a dind container takes a few seconds to fully initialize. Your job might be trying to connect before it's ready.
Add a wait loop in your CI script:
#!/bin/bash
# wait-for-docker.sh
echo "Waiting for Docker daemon..."
TIMEOUT=30
ELAPSED=0
while ! docker info >/dev/null 2>&1; do
if [ $ELAPSED -ge $TIMEOUT ]; then
echo "Timeout waiting for Docker daemon"
exit 1
fi
echo "Docker not ready, waiting..."
sleep 2
ELAPSED=$((ELAPSED + 2))
done
echo "Docker daemon is ready!"
docker versionOr as a one-liner:
timeout 30 sh -c 'until docker info >/dev/null 2>&1; do sleep 1; done'If you need Docker Desktop to listen on TCP (for WSL1, IDEs, or other tools):
Docker Desktop on Windows/macOS:
1. Open Docker Desktop
2. Click the gear icon (Settings)
3. Enable "Expose daemon on tcp://localhost:2375 without TLS"
4. Click Apply & Restart
Verify it's working:
curl http://localhost:2375/version
# or
docker -H tcp://localhost:2375 versionSecurity Warning: This exposes the Docker API without authentication. Only enable this on trusted networks and never on publicly accessible machines.
On Linux, Docker listens only on the Unix socket by default. To enable TCP:
Method 1: Systemd override (recommended)
Create an override file:
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo tee /etc/systemd/system/docker.service.d/override.conf << EOF
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://127.0.0.1:2375
EOFReload and restart Docker:
sudo systemctl daemon-reload
sudo systemctl restart dockerMethod 2: daemon.json
Edit /etc/docker/daemon.json:
{
"hosts": ["unix:///var/run/docker.sock", "tcp://127.0.0.1:2375"]
}Important: If using daemon.json, you may need to remove the -H flag from the systemd service file to avoid conflicts.
Restart Docker:
sudo systemctl restart dockerVerify TCP is listening:
ss -tlnp | grep 2375
curl http://127.0.0.1:2375/versionFirewall rules might be blocking the Docker TCP port:
On Linux with UFW:
# Check current rules
sudo ufw status
# Allow port 2375 (only if needed for remote access)
sudo ufw allow 2375/tcpOn Linux with firewalld:
# Check if port is allowed
sudo firewall-cmd --list-ports
# Allow port 2375
sudo firewall-cmd --permanent --add-port=2375/tcp
sudo firewall-cmd --reloadOn Windows:
# Check if port is blocked
netsh advfirewall firewall show rule name=all | findstr "2375"
# Allow port (PowerShell as Administrator)
New-NetFirewallRule -DisplayName "Docker TCP" -Direction Inbound -LocalPort 2375 -Protocol TCP -Action AllowNote: Only open firewall ports if you specifically need remote Docker access. For local-only access, binding to 127.0.0.1 is sufficient.
To diagnose network issues between client and daemon:
Test if the port is reachable:
# Using netcat
nc -zv docker 2375
nc -zv localhost 2375
# Using curl
curl -v http://docker:2375/version
curl -v http://localhost:2375/version
# Using telnet
telnet docker 2375Check what's listening on the port:
# Linux
ss -tlnp | grep 2375
netstat -tlnp | grep 2375
# Windows
netstat -ano | findstr 2375In Docker/Kubernetes environments, verify DNS resolution:
# Check if 'docker' hostname resolves
getent hosts docker
nslookup docker
# In Kubernetes, check service discovery
kubectl get svc
kubectl get endpointsUnderstanding TCP vs Unix Socket connections:
Docker supports two main connection methods:
- Unix socket (/var/run/docker.sock): Default on Linux/macOS, more secure, local only
- TCP (tcp://host:port): Required for remote access, CI/CD, and some tools
Security implications of TCP access:
Exposing Docker over TCP port 2375 (without TLS) is extremely dangerous on untrusted networks. Anyone with network access can:
- Run containers with full root privileges on the host
- Access all images, containers, volumes, and networks
- Mount the host filesystem and read/modify any files
- Use the host's network stack for attacks
For production environments, always use TLS (port 2376):
# Generate TLS certificates
docker run --rm -v ~/.docker:/certs paulczar/omgwtfssl
# Configure daemon for TLS
# /etc/docker/daemon.json
{
"hosts": ["unix:///var/run/docker.sock", "tcp://0.0.0.0:2376"],
"tls": true,
"tlscacert": "/etc/docker/ca.pem",
"tlscert": "/etc/docker/server-cert.pem",
"tlskey": "/etc/docker/server-key.pem",
"tlsverify": true
}
# Client configuration
export DOCKER_HOST=tcp://hostname:2376
export DOCKER_TLS_VERIFY=1
export DOCKER_CERT_PATH=~/.dockerSSH-based Docker access (most secure for remote):
Instead of TCP, consider SSH tunneling:
# Use SSH directly
export DOCKER_HOST=ssh://user@remotehost
docker ps
# Or create a Docker context
docker context create remote --docker "host=ssh://user@remotehost"
docker context use remoteDebugging dind in CI/CD:
If dind is not starting properly, check the service logs:
# GitLab CI - add debug output
script:
- docker logs $(docker ps -q --filter ancestor=docker:dind) || echo "No dind container found"
- cat /var/log/docker.log || true
- dmesg | tail -20 || trueCommon dind issues:
- Missing --privileged flag for the dind container
- Storage driver incompatibility (overlay2 requires specific kernel support)
- Insufficient memory or disk space in the CI runner
- Kernel module ip_tables not loaded in the host
image operating system "linux" cannot be used on this platform
How to fix 'image operating system linux cannot be used on this platform' in Docker
manifest unknown: manifest unknown
How to fix 'manifest unknown' in Docker
cannot open '/etc/passwd': Permission denied
How to fix 'cannot open: Permission denied' in Docker
Error response from daemon: failed to create the ipvlan port
How to fix 'failed to create the ipvlan port' in Docker
toomanyrequests: Rate exceeded for anonymous users
How to fix 'Rate exceeded for anonymous users' in Docker Hub