Skip to content

Last updated: 2026-06-25

Docker TLS/SSL Configuration Guide

This guide provides recommended TLS/SSL settings for securing the Docker daemon API with TLS. By default, the Docker daemon listens on a Unix socket. When exposing it over a network (TCP), TLS with client certificate authentication is essential to prevent unauthorized access; anyone with network access to an unprotected Docker daemon has full root access to the host.

Prerequisites

  • Docker Engine 28 or later (29.x current)
  • OpenSSL 1.1.1 or later

Certificate Generation

Docker TLS requires a CA, server certificate, and client certificate. The server certificate authenticates the daemon, and the client certificate authenticates the user.

Create a CA

openssl genrsa -aes256 -out ca-key.pem 4096
openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem \
    -subj "/CN=Docker CA"

Create the Server Certificate

Create a server key and certificate signing request:

openssl genrsa -out server-key.pem 4096
openssl req -new -key server-key.pem -sha256 -out server.csr \
    -subj "/CN=docker.example.com"

Create a SAN extension file to include all hostnames and IPs the daemon will be accessed by:

echo "subjectAltName = DNS:docker.example.com,IP:10.0.0.1,IP:127.0.0.1" > extfile-server.cnf
echo "extendedKeyUsage = serverAuth" >> extfile-server.cnf

Sign the server certificate:

openssl x509 -req -days 365 -sha256 -in server.csr \
    -CA ca.pem -CAkey ca-key.pem -CAcreateserial \
    -out server-cert.pem -extfile extfile-server.cnf

Create the Client Certificate

openssl genrsa -out client-key.pem 4096
openssl req -new -key client-key.pem -sha256 -out client.csr \
    -subj "/CN=client"

echo "extendedKeyUsage = clientAuth" > extfile-client.cnf

openssl x509 -req -days 365 -sha256 -in client.csr \
    -CA ca.pem -CAkey ca-key.pem -CAcreateserial \
    -out client-cert.pem -extfile extfile-client.cnf

Set Permissions

chmod 600 ca-key.pem server-key.pem client-key.pem
chmod 644 ca.pem server-cert.pem client-cert.pem

Install Certificates

Copy server certificates to the Docker host:

mkdir -p /etc/docker/ssl
cp ca.pem server-cert.pem server-key.pem /etc/docker/ssl/

Copy client certificates to each machine that needs Docker API access:

mkdir -p ~/.docker
cp ca.pem client-cert.pem client-key.pem ~/.docker/

Daemon Configuration

Configure the Docker daemon to require TLS for all TCP connections. Edit /etc/docker/daemon.json:

{
    "tls": true,
    "tlsverify": true,
    "tlscacert": "/etc/docker/ssl/ca.pem",
    "tlscert": "/etc/docker/ssl/server-cert.pem",
    "tlskey": "/etc/docker/ssl/server-key.pem",
    "hosts": ["unix:///var/run/docker.sock", "tcp://[::]:2376"]
}
  • tls: true enables TLS encryption.
  • tlsverify: true requires clients to present a valid certificate signed by the CA. This is critical; without tlsverify, any client can connect.
  • hosts lists the interfaces to listen on. Port 2376 is the standard TLS-secured Docker port (2375 is the unencrypted port and should never be exposed).

If your Docker service is started by systemd and the unit file includes -H flags, you may need to override the service file to avoid conflicts with the hosts setting in daemon.json. Create an override:

mkdir -p /etc/systemd/system/docker.service.d
cat > /etc/systemd/system/docker.service.d/override.conf << 'EOF'
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd
EOF
systemctl daemon-reload

Client Configuration

Environment Variables

Set environment variables to tell the Docker CLI to use TLS:

export DOCKER_HOST=tcp://docker.example.com:2376
export DOCKER_TLS_VERIFY=1
export DOCKER_CERT_PATH=~/.docker

Command-Line Flags

Or pass TLS options directly:

docker --tlsverify \
    --tlscacert=~/.docker/ca.pem \
    --tlscert=~/.docker/client-cert.pem \
    --tlskey=~/.docker/client-key.pem \
    -H tcp://docker.example.com:2376 info

Docker Context

Create a named context for the TLS-secured host:

docker context create secure-host \
    --docker "host=tcp://docker.example.com:2376,ca=~/.docker/ca.pem,cert=~/.docker/client-cert.pem,key=~/.docker/client-key.pem"

docker context use secure-host

Docker Compose

Docker Compose uses the same environment variables:

export DOCKER_HOST=tcp://docker.example.com:2376
export DOCKER_TLS_VERIFY=1
export DOCKER_CERT_PATH=~/.docker
docker compose up -d

Security Considerations

  • Never expose port 2375 (unencrypted Docker API) to any network. An unprotected Docker socket grants root access to the host.
  • Always use tlsverify, not just tls. The tls flag alone encrypts traffic but does not authenticate clients.
  • Protect the CA key (ca-key.pem). Anyone with the CA key can generate client certificates and access the daemon.
  • Use short-lived certificates and rotate them regularly.
  • Prefer the Unix socket for local access. Only expose TCP when remote access is required.

See RFC 8446 ยง4.3.2 for the TLS Certificate Request specification, and Wikipedia: Mutual authentication for a general overview.

Security Notes

The Docker daemon uses Go's crypto/tls package, which has a different vulnerability history than OpenSSL:

  • POODLE (CVE-2014-3566, 2014): SSL 3.0 has never been supported in Go's TLS implementation.
  • BEAST (CVE-2011-3389, 2011): Mitigated by recommending TLS 1.2 as the minimum; AEAD-only ciphers eliminate the CBC padding oracle.
  • CRIME (CVE-2012-4929, 2012): TLS compression is not supported in Go's crypto/tls.
  • Lucky13 (2013): AEAD-only cipher list eliminates CBC padding timing side-channels entirely.
  • FREAK (CVE-2015-0204, 2015): EXPORT-grade ciphers have never been supported in Go's crypto/tls.
  • LOGJAM (CVE-2015-4000, 2015): DHE key exchange is not offered by default in Go; only ECDHE is used.
  • Sweet32 (CVE-2016-2183, 2016): 3DES was removed from Go's default cipher list in Go 1.23 (August 2024) and is excluded from the recommended configuration.
  • ROBOT (2017): Static RSA key exchange is not offered in Go's crypto/tls; only ECDHE is used.
  • Downgrade attacks: Go's crypto/tls does not perform insecure version-fallback retries, and TLS 1.3 has built-in downgrade protection.

The following are not addressable through TLS configuration alone:

  • Heartbleed (CVE-2014-0160, 2014): Not applicable. Go's crypto/tls is an independent TLS implementation and was never affected by Heartbleed.
  • BREACH (CVE-2013-3587, 2013): Not applicable. The Docker Remote API exchanges JSON but does not use HTTP response compression in practice; the attack surface is not present under normal usage.
  • DROWN (CVE-2016-0800, 2016): Not applicable. Go's crypto/tls does not support SSLv2.

Verification

Restart Docker:

systemctl restart docker

Test the TLS connection:

docker --tlsverify -H tcp://docker.example.com:2376 info

Verify with OpenSSL:

openssl s_client -connect docker.example.com:2376 -cert ~/.docker/client-cert.pem -key ~/.docker/client-key.pem -CAfile ~/.docker/ca.pem

Verify a connection without a client certificate is rejected:

curl https://docker.example.com:2376/info --cacert ~/.docker/ca.pem

This should fail with a TLS handshake error, confirming client certificate verification is enforced.


Related Guides

View all Infrastructure guides →

Configured TLS? Now Monitor It.

Generator Labs alerts you before certificates expire, get revoked, or fail chain validation, across HTTPS, SMTPS, IMAPS, LDAPS, and more.

Certificate Monitoring →