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:passwordWarning
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:latestdocker-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=passwordTip
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:5000nginx/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.localTrust 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.crtDockerfile 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=defaultReference 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: 80Mount 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-tlsTrust 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.