Back to Guides
Certificates > Guides > Docker

Docker Certificate Management

How to handle TLS certificates in Docker containers: generating certs, mounting volumes, building custom CA trust into images, Docker Compose TLS, and Kubernetes secrets.

Generate Certs in a Dockerfile

Generate self-signed certificates at build time using OpenSSL.

Dockerfile with certificate generation:

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base

# Install openssl
RUN apt-get update && apt-get install -y openssl && rm -rf /var/lib/apt/lists/*

# Generate a self-signed certificate
RUN openssl req -x509 -new -nodes \
  -keyout /app/certs/server.key \
  -out /app/certs/server.crt \
  -days 365 \
  -subj "/CN=myapp/O=Dev" \
  -addext "subjectAltName=DNS:myapp,DNS:localhost,IP:127.0.0.1"

# Create PFX for .NET
RUN openssl pkcs12 -export \
  -in /app/certs/server.crt \
  -inkey /app/certs/server.key \
  -out /app/certs/server.pfx \
  -passout pass:password

Warning

Generating certificates at build time bakes them into the image layer. For production, mount certificates as volumes or use a secrets manager.

Mount Certificates as Volumes

docker run

Mount a certificate directory into the container:

docker run -d \
  -p 5001:5001 \
  -v $(pwd)/certs:/app/certs:ro \
  -e ASPNETCORE_Kestrel__Endpoints__Https__Url=https://0.0.0.0:5001 \
  -e ASPNETCORE_Kestrel__Endpoints__Https__Certificate__Path=/app/certs/server.pfx \
  -e ASPNETCORE_Kestrel__Endpoints__Https__Certificate__Password=password \
  myapp:latest

docker-compose volumes

Mount certificates in docker-compose.yml:

services:
  myapp:
    image: myapp:latest
    ports:
      - "5001:5001"
    volumes:
      - ./certs:/app/certs:ro
    environment:
      - ASPNETCORE_Kestrel__Endpoints__Https__Url=https://0.0.0.0:5001
      - ASPNETCORE_Kestrel__Endpoints__Https__Certificate__Path=/app/certs/server.pfx
      - ASPNETCORE_Kestrel__Endpoints__Https__Certificate__Password=password

Tip

Use :ro (read-only) when mounting certificates. The container should never need to modify them.

Add a Custom CA to a Docker Image

If your container needs to trust a custom CA (e.g., for internal services or corporate proxies), add it to the image's trust store.

Debian/Ubuntu-based images

Dockerfile to add a custom CA:

FROM mcr.microsoft.com/dotnet/aspnet:8.0

# Copy custom CA certificate
COPY myca.crt /usr/local/share/ca-certificates/myca.crt

# Update the trust store
RUN update-ca-certificates

COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApp.dll"]

Alpine-based images

Dockerfile for Alpine:

FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine

# Copy custom CA
COPY myca.crt /usr/local/share/ca-certificates/myca.crt

# Alpine uses a different update command
RUN apk add --no-cache ca-certificates && update-ca-certificates

COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApp.dll"]

Docker Compose with TLS

A complete Docker Compose setup with a .NET API behind an Nginx reverse proxy with TLS termination.

docker-compose.yml with TLS:

services:
  nginx:
    image: nginx:alpine
    ports:
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - ./certs/server.crt:/etc/nginx/ssl/server.crt:ro
      - ./certs/server.key:/etc/nginx/ssl/server.key:ro
    depends_on:
      - api

  api:
    build: .
    expose:
      - "5000"
    environment:
      - ASPNETCORE_URLS=http://0.0.0.0:5000

nginx/nginx.conf:

server {
    listen 443 ssl;
    server_name localhost;

    ssl_certificate     /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
    ssl_protocols       TLSv1.2 TLSv1.3;

    location / {
        proxy_pass http://api:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

mkcert for Local Docker Development

mkcert generates locally-trusted certificates without manual CA setup. It automatically installs a root CA in your system trust store.

Install and use mkcert:

# Install (macOS)
brew install mkcert

# Install the local CA
mkcert -install

# Generate certs for your local services
mkcert -cert-file certs/server.crt -key-file certs/server.key \
  localhost 127.0.0.1 myapp.local "*.myapp.local"

# Generate a PFX for .NET
mkcert -p12-file certs/server.pfx \
  localhost 127.0.0.1 myapp.local

Trust mkcert CA inside a Docker container:

# Get the CA cert location
mkcert -CAROOT
# e.g., /Users/you/Library/Application Support/mkcert

# Copy the root CA into your Docker build context
cp "$(mkcert -CAROOT)/rootCA.pem" ./certs/mkcert-ca.crt

Dockerfile with mkcert CA:

FROM mcr.microsoft.com/dotnet/aspnet:8.0

# Trust the mkcert CA inside the container
COPY certs/mkcert-ca.crt /usr/local/share/ca-certificates/mkcert-ca.crt
RUN update-ca-certificates

COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApp.dll"]

Tip

With mkcert, your locally running browser trusts the cert (because mkcert installed the CA), and the Docker container also trusts it (because you copied the CA into the image). No more browser warnings or validation errors.

Kubernetes TLS Secrets

Create a TLS secret from certificate files:

kubectl create secret tls myapp-tls \
  --cert=server.crt \
  --key=server.key \
  --namespace=default

Reference the TLS secret in an Ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp-ingress
spec:
  tls:
    - hosts:
        - myapp.example.com
      secretName: myapp-tls
  rules:
    - host: myapp.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: myapp
                port:
                  number: 80

Mount a TLS secret as files in a pod:

apiVersion: v1
kind: Pod
metadata:
  name: myapp
spec:
  containers:
    - name: myapp
      image: myapp:latest
      volumeMounts:
        - name: tls-certs
          mountPath: /app/certs
          readOnly: true
  volumes:
    - name: tls-certs
      secret:
        secretName: myapp-tls

Trust Chain for Inter-Container Communication

When containers need to communicate over TLS (e.g., API to database, service-to-service), every container that initiates a TLS connection must trust the CA that signed the target's certificate.

docker-compose.yml with shared CA trust:

services:
  api:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ./certs/api.pfx:/app/certs/api.pfx:ro
      - ./certs/ca.crt:/usr/local/share/ca-certificates/internal-ca.crt:ro
    # Run update-ca-certificates at startup
    command: >
      sh -c "update-ca-certificates && dotnet MyApp.dll"
    environment:
      - ASPNETCORE_Kestrel__Endpoints__Https__Certificate__Path=/app/certs/api.pfx
      - ASPNETCORE_Kestrel__Endpoints__Https__Certificate__Password=password

  worker:
    build:
      context: ./worker
    volumes:
      - ./certs/ca.crt:/usr/local/share/ca-certificates/internal-ca.crt:ro
    command: >
      sh -c "update-ca-certificates && dotnet Worker.dll"

Tip

For local development, consider using a single shared CA (generated with mkcert or openssl) to sign all service certificates. Mount that CA into every container that needs to make outbound TLS calls.

Warning

Running update-ca-certificates at container startup adds latency. For production, bake the CA into the image at build time instead.