Intro
Managing Docker across multiple hosts in a homelab requires secure remote access to the Docker daemon. The standard approach is to expose the daemon over TLS with certificate-based authentication. This ensures traffic is encrypted and both client and server verify each other’s identity. This post covers the TLS setup I use for homelab environments where I need to manage containers across multiple machines.
See the official Docker Security Guide for reference: Protect the Docker daemon socket
Security Model
The model is straightforward:
- A Certificate Authority (CA) issues certificates
- The Docker host gets a server certificate signed by the CA
- Each client gets its own certificate signed by the CA
- Docker daemon requires TLS and validates certificates
This prevents unauthorized access and eavesdropping on the Docker socket.
Certificate Generation
I generate three sets of certificates: CA, server (daemon), and client. All use 4096-bit RSA for stronger security.
1. Create the Certificate Authority
openssl genrsa -aes256 -out ca-key.pem 4096
# Enter passphrase when prompted
Then create the CA certificate:
openssl req -new -x509 -days 3650 -key ca-key.pem -sha256 -out ca.pem
# Enter CA passphrase
# Fill in organizational details:
# Country: US, State: California, City: Example
# Organization: Example Organization, Unit: IT
# Common Name: docker-ca
# Email: admin@example.com
2. Create the Server (Daemon) Certificate
Generate the server’s private key:
openssl genrsa -out server-key.pem 4096
Create a certificate signing request for your Docker host:
openssl req -subj "/CN=docker-host.example.com" -sha256 -new \
-key server-key.pem -out server.csr
Add the Subject Alternative Name (SAN) to include the IP address and/or hostname:
echo "subjectAltName = DNS:docker-host.example.com,IP:203.0.113.42,IP:127.0.0.1" >> extfile.cnf
echo "extendedKeyUsage = serverAuth" >> extfile.cnf
Sign the server certificate with the CA:
openssl x509 -req -days 3650 -sha256 -in server.csr \
-CA ca.pem -CAkey ca-key.pem -CAcreateserial \
-out server-cert.pem -extfile extfile.cnf
# Enter CA passphrase when prompted
3. Create a Client Certificate
Generate the client’s private key:
openssl genrsa -out client-key.pem 4096
Create client certificate signing request:
openssl req -subj "/CN=client" -new -key client-key.pem -out client.csr
Add client-specific attributes:
echo "extendedKeyUsage = clientAuth" > extfile-client.cnf
Sign the client certificate:
openssl x509 -req -days 3650 -sha256 -in client.csr \
-CA ca.pem -CAkey ca-key.pem -CAcreateserial \
-out client-cert.pem -extfile extfile-client.cnf
# Enter CA passphrase when prompted
4. Cleanup
Remove temporary CSR files:
rm -v client.csr server.csr extfile.cnf extfile-client.cnf
5. Set Proper Permissions
Restrict private key access and make certs world-readable where needed:
chmod -v 0400 ca-key.pem client-key.pem server-key.pem
chmod -v 0444 ca.pem server-cert.pem client-cert.pem
Configure Docker Daemon
Store certificates in a dedicated directory (e.g., /etc/docker/tls/) and configure the Docker daemon to use them.
Edit the Docker service configuration:
sudo systemctl edit docker.service
Add or modify the ExecStart line in the [Service] section:
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd \
-H fd:// \
-H tcp://203.0.113.42:2376 \
--tlsverify \
--tlscacert=/etc/docker/tls/ca.pem \
--tlscert=/etc/docker/tls/server-cert.pem \
--tlskey=/etc/docker/tls/server-key.pem
Key flags:
-H tcp://...exposes the daemon on the network interface and port--tlsverifyrequires client certificates--tlscacertvalidates client certificates--tlscertand--tlskeyare the server credentials
Reload and restart:
sudo systemctl daemon-reload
sudo systemctl restart docker.service
Verify the daemon is listening:
sudo netstat -lntp | grep dockerd
You should see the daemon listening on port 2376 (default TLS port).
Using Client Certificates
On a remote client, copy the client certificates and connect:
# Setup local directory
mkdir -p ~/.docker/tls/docker-host.example.com/
cp /path/to/ca.pem ~/.docker/tls/docker-host.example.com/
cp /path/to/client-cert.pem ~/.docker/tls/docker-host.example.com/
cp /path/to/client-key.pem ~/.docker/tls/docker-host.example.com/
Set environment variables:
export DOCKER_HOST=tcp://docker-host.example.com:2376
export DOCKER_CERT_PATH=~/.docker/tls/docker-host.example.com
export DOCKER_TLS_VERIFY=1
Then use Docker commands normally:
docker ps
docker images
Or set these in your shell profile or Docker CLI config for persistence.