Docker TLS & Certificate Commands

Comprehensive guide to Docker TLS configuration, certificate management, and secure registry operations

🔒 Docker Daemon TLS Configuration

Generate CA (Certificate Authority)

# Generate CA private key
openssl genrsa -aes256 -out ca-key.pem 4096

# Generate CA certificate
openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem \
  -subj "/C=US/ST=CA/L=San Francisco/O=Example/CN=Docker CA"

Creates a Certificate Authority for signing Docker daemon and client certificates.

Generate server key and certificate

# Generate server private key
openssl genrsa -out server-key.pem 4096

# Create certificate signing request
openssl req -subj "/CN=docker-host" -sha256 -new -key server-key.pem -out server.csr

# Add subject alternative names (replace with your IPs/hostnames)
echo "subjectAltName = DNS:docker-host,IP:10.0.0.1,IP:127.0.0.1" > extfile.cnf
echo "extendedKeyUsage = serverAuth" >> extfile.cnf

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

Creates server certificate with multiple hostnames/IPs for Docker daemon.

Generate client key and certificate

# Generate client private key
openssl genrsa -out key.pem 4096

# Create certificate signing request
openssl req -subj '/CN=client' -new -key key.pem -out client.csr

# Sign client certificate
echo "extendedKeyUsage = clientAuth" > extfile-client.cnf
openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem \
  -CAcreateserial -out cert.pem -extfile extfile-client.cnf

Configure daemon.json for TLS

{
  "tlsverify": true,
  "tlscacert": "/etc/docker/certs/ca.pem",
  "tlscert": "/etc/docker/certs/server-cert.pem",
  "tlskey": "/etc/docker/certs/server-key.pem",
  "hosts": ["tcp://0.0.0.0:2376", "unix:///var/run/docker.sock"]
}

Place in /etc/docker/daemon.json and restart Docker: sudo systemctl restart docker

Connect to Docker daemon with TLS

docker --tlsverify \
  --tlscacert=ca.pem \
  --tlscert=cert.pem \
  --tlskey=key.pem \
  -H=docker-host:2376 version

Set environment variables for TLS

export DOCKER_HOST=tcp://docker-host:2376
export DOCKER_TLS_VERIFY=1
export DOCKER_CERT_PATH=/path/to/certs

# Now use docker commands normally
docker ps

🏪 Docker Registry TLS Configuration

Run registry with TLS (Let's Encrypt)

docker run -d \
  --name registry \
  -p 5000:5000 \
  -v /path/to/certs:/certs \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/fullchain.pem \
  -e REGISTRY_HTTP_TLS_KEY=/certs/privkey.pem \
  registry:2

Uses Let's Encrypt certificates for secure registry.

Run registry with self-signed certificate

# Generate self-signed certificate
openssl req -newkey rsa:4096 -nodes -sha256 \
  -keyout domain.key -x509 -days 365 -out domain.crt \
  -subj "/CN=registry.example.com"

# Run registry
docker run -d \
  --name registry \
  -p 5000:5000 \
  -v /path/to/certs:/certs \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
  -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
  registry:2

Add self-signed CA to Docker trust store (Linux)

# Copy CA certificate
sudo mkdir -p /etc/docker/certs.d/registry.example.com:5000
sudo cp ca.crt /etc/docker/certs.d/registry.example.com:5000/ca.crt

# Restart Docker
sudo systemctl restart docker

Add self-signed CA to system trust store (Ubuntu/Debian)

sudo cp ca.crt /usr/local/share/ca-certificates/docker-registry-ca.crt
sudo update-ca-certificates
sudo systemctl restart docker

Add self-signed CA to macOS trust store

sudo security add-trusted-cert -d -r trustRoot \
  -k /Library/Keychains/System.keychain ca.crt

Test registry connectivity

curl https://registry.example.com:5000/v2/

Should return: {}

📦 Container Certificate Management

Mount certificates as volumes

docker run -d \
  -v /host/certs:/app/certs:ro \
  -e SSL_CERT_FILE=/app/certs/cert.pem \
  -e SSL_KEY_FILE=/app/certs/key.pem \
  myapp:latest

Mounts certificates as read-only volume for application use.

Add CA certificates to container

# In Dockerfile
FROM alpine:latest
COPY ca-cert.crt /usr/local/share/ca-certificates/
RUN update-ca-certificates

Add CA certificates to running container

docker cp ca-cert.crt container_name:/usr/local/share/ca-certificates/
docker exec container_name update-ca-certificates

Copy certificates into image (not recommended for production)

# In Dockerfile
FROM node:18
COPY certs/ /app/certs/
RUN chmod 600 /app/certs/key.pem

⚠️ Warning: Certificates will be in image layers. Use secrets or volumes instead.

🔐 Docker Secrets (Swarm)

Create secret from file

docker secret create ssl_cert cert.pem

Create secret from stdin

cat cert.pem | docker secret create ssl_cert -

List secrets

docker secret ls

Use secrets in service

docker service create \
  --name myapp \
  --secret source=ssl_cert,target=/run/secrets/cert.pem \
  --secret source=ssl_key,target=/run/secrets/key.pem \
  myapp:latest

Secrets are mounted at /run/secrets/ by default.

Use secrets in docker-compose.yml

version: '3.8'
services:
  app:
    image: myapp:latest
    secrets:
      - ssl_cert
      - ssl_key
    environment:
      - SSL_CERT_FILE=/run/secrets/ssl_cert
      - SSL_KEY_FILE=/run/secrets/ssl_key

secrets:
  ssl_cert:
    file: ./certs/cert.pem
  ssl_key:
    file: ./certs/key.pem

Remove secret

docker secret rm ssl_cert

🔑 Registry Authentication with Certificates

Login to private registry

docker login registry.example.com:5000

Login with credentials from file

docker login --username myuser --password-stdin registry.example.com < password.txt

Configure registry authentication in config.json

{
  "auths": {
    "registry.example.com:5000": {
      "auth": "base64(username:password)"
    }
  }
}

Located at ~/.docker/config.json

Pull from private registry

docker pull registry.example.com:5000/myapp:latest

Push to private registry

docker tag myapp:latest registry.example.com:5000/myapp:latest
docker push registry.example.com:5000/myapp:latest

📦 Docker Compose TLS Configuration

Nginx reverse proxy with TLS (docker-compose.yml)

services:
  nginx:
    image: nginx:alpine
    ports:
      - "443:443"
    volumes:
      - ./certs:/etc/nginx/certs:ro
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    restart: unless-stopped

  app:
    build: .
    expose:
      - "3000"

Traefik with automatic TLS (docker-compose.yml)

services:
  traefik:
    image: traefik:v2
    command:
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.myresolver.acme.tlschallenge=true"
      - "[email protected]"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
    ports:
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt

  app:
    image: myapp:latest
    labels:
      - "traefik.http.routers.app.rule=Host(`example.com`)"
      - "traefik.http.routers.app.tls.certresolver=myresolver"

Traefik automatically obtains and renews certificates via ACME TLS challenge.

🔧 Troubleshooting TLS Issues

Error: "x509: certificate signed by unknown authority"

Docker doesn't trust the CA. Add CA to Docker's trust store:

sudo mkdir -p /etc/docker/certs.d/registry.example.com:5000
sudo cp ca.crt /etc/docker/certs.d/registry.example.com:5000/ca.crt
sudo systemctl restart docker

Error: "certificate is valid for ..., not ..."

Certificate's Common Name (CN) or Subject Alternative Names (SAN) don't match. Regenerate certificate with correct hostnames/IPs.

Test TLS connection to registry

openssl s_client -connect registry.example.com:5000 -showcerts

Test registry API

# Test catalog
curl https://registry.example.com:5000/v2/_catalog

# Test specific image
curl https://registry.example.com:5000/v2/myapp/tags/list

Check Docker TLS configuration

docker info | grep -i tls

Disable TLS verification (testing only)

# Add to /etc/docker/daemon.json
{
  "insecure-registries": ["registry.example.com:5000"]
}

⚠️ Warning: Only for testing. Never use in production.

✅ Best Practices

Certificate management

  • Use Let's Encrypt for public registries (automatic renewal)
  • Use 4096-bit RSA or EC P-256 keys minimum
  • Set reasonable expiry dates (90-365 days) and automate renewal
  • Never commit certificates or private keys to version control
  • Use Docker secrets or external secret management (Vault, AWS Secrets Manager)

Security

  • Always enable TLS verification (no insecure registries in production)
  • Use Docker secrets instead of environment variables for sensitive data
  • Rotate certificates before expiry (automated with Let's Encrypt)
  • Use read-only mounts for certificates in containers
  • Implement certificate pinning for high-security environments

Production readiness

  • Use proper CA-signed certificates (not self-signed) in production
  • Monitor certificate expiry with alerting (30/7/1 day warnings)
  • Document certificate locations and renewal procedures
  • Test certificate rotation procedures regularly
  • Keep backup copies of certificates in secure location