How to Securely Expose Docker API via TCP for Remote Management (Portainer/CLI)


1 views

When troubleshooting Docker API connectivity issues, first check the active configuration:

sudo systemctl status docker
ps aux | grep dockerd

If you see -H fd:// as the only socket configuration, this explains why TCP connections fail. Modern Docker installations often default to Unix socket-only configuration.

For most modern Linux distributions using systemd, modify the service configuration:

sudo mkdir -p /etc/systemd/system/docker.service.d
sudo nano /etc/systemd/system/docker.service.d/override.conf

Add these contents:

[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2375 --containerd=/run/containerd/containerd.sock

Then reload and restart:

sudo systemctl daemon-reload
sudo systemctl restart docker

For systems using SysVinit, edit /etc/default/docker:

DOCKER_OPTS="-H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock"

Restart with:

sudo service docker restart

Never expose Docker API publicly without protection. For production environments:

# Create certs directory
sudo mkdir -p /etc/docker/certs
sudo chmod 700 /etc/docker/certs

# Generate certificates
openssl req -newkey rsa:4096 -nodes -sha256 \
  -keyout /etc/docker/certs/domain.key \
  -x509 -days 365 -out /etc/docker/certs/domain.crt

Configure TLS in docker.service:

ExecStart=/usr/bin/dockerd \
  -H fd:// \
  -H tcp://0.0.0.0:2376 \
  --tlsverify \
  --tlscacert=/etc/docker/certs/ca.pem \
  --tlscert=/etc/docker/certs/server-cert.pem \
  --tlskey=/etc/docker/certs/server-key.pem

Verify from another machine:

docker -H tcp://your_server_ip:2375 version

# With TLS:
docker --tlsverify \
  --tlscacert=ca.pem \
  --tlscert=cert.pem \
  --tlskey=key.pem \
  -H=tcp://your_server_ip:2376 version

Ensure the port is accessible:

sudo ufw allow 2375/tcp   # For non-TLS
sudo ufw allow 2376/tcp   # For TLS
sudo ufw reload

For CentOS/RHEL:

sudo firewall-cmd --permanent --add-port=2375/tcp
sudo firewall-cmd --reload

When adding the endpoint in Portainer:
1. Use http://your_server_ip:2375 for non-TLS
2. Use https://your_server_ip:2376 for TLS
3. Upload client certificates when prompted for secure connections


When attempting to configure Docker for remote management via TCP (particularly for Portainer integration), many users encounter connectivity issues despite seemingly correct configurations. The fundamental challenge lies in properly exposing the Docker daemon's API endpoint while maintaining security.

Recent Docker versions (17.04+) have moved away from modifying /etc/default/docker or init scripts. The preferred approach now uses systemd drop-in files:

sudo mkdir -p /etc/systemd/system/docker.service.d
sudo nano /etc/systemd/system/docker.service.d/override.conf

Add these contents:

[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2375 --containerd=/run/containerd/containerd.sock

After making changes, always verify with:

sudo systemctl daemon-reload
sudo systemctl restart docker
sudo netstat -tulnp | grep 2375

Exposing Docker's API unsecured is dangerous. Always implement:

  1. TLS encryption (create certs with openssl)
  2. Firewall rules limiting access
  3. Network segmentation

Sample TLS configuration:

ExecStart=/usr/bin/dockerd \
  --tlsverify \
  --tlscacert=/etc/docker/ca.pem \
  --tlscert=/etc/docker/server-cert.pem \
  --tlskey=/etc/docker/server-key.pem \
  -H tcp://0.0.0.0:2376 \
  -H unix:///var/run/docker.sock

When connections fail:

# Check daemon status
sudo systemctl status docker

# Test local connection
curl http://localhost:2375/version

# Check firewall rules
sudo ufw status
sudo iptables -L -n

For Portainer to work with remote endpoints:

docker run -d -p 9000:9000 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v portainer_data:/data \
  portainer/portainer \
  -H tcp://:2375

A more secure alternative to direct TCP exposure:

ssh -NL localhost:2375:/var/run/docker.sock user@remote-host

Then connect to localhost:2375 from your local machine.