Docker 容器通常不直接处理 SSL——前面的反向代理负责终结 TLS 并将解密后的流量转发给容器。本指南涵盖三种最常见的方案。
方案一:Nginx 反向代理 + 手动证书
最适合小型部署的简单方案。从 GetHTTPS 获取证书并挂载到 Nginx 容器中。
docker-compose.yml
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./certs/fullchain.pem:/etc/ssl/fullchain.pem:ro
- ./certs/privkey.pem:/etc/ssl/privkey.pem:ro
depends_on:
- app
app:
image: your-app:latest
expose:
- "3000"
nginx.conf
server {
listen 443 ssl http2;
server_name yourdomain.com;
ssl_certificate /etc/ssl/fullchain.pem;
ssl_certificate_key /etc/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
proxy_pass http://app:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 80;
server_name yourdomain.com;
return 301 https://$host$request_uri;
}
续签方式: 用来自 GetHTTPS 的新文件替换 ./certs/ 中的文件,然后执行 docker compose exec nginx nginx -s reload。
方案二:Traefik 自动获取 Let’s Encrypt
Traefik 是为容器构建的反向代理,可以自动获取和续签 Let’s Encrypt 证书。
docker-compose.yml
services:
traefik:
image: traefik:v3.0
command:
- "--providers.docker=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.email=you@example.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/acme/acme.json"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- traefik-acme:/acme
app:
image: your-app:latest
labels:
- "traefik.http.routers.app.rule=Host(`yourdomain.com`)"
- "traefik.http.routers.app.tls.certresolver=letsencrypt"
- "traefik.http.services.app.loadbalancer.server.port=3000"
volumes:
traefik-acme:
Traefik 处理一切:证书签发、续签和 HTTPS 终结。无需手动管理证书。
方案三:Caddy(零配置 HTTPS)
Caddy 自动获取和续签 Let’s Encrypt 证书,完全零配置。
docker-compose.yml
services:
caddy:
image: caddy:2
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy-data:/data
depends_on:
- app
app:
image: your-app:latest
expose:
- "3000"
volumes:
caddy-data:
Caddyfile
yourdomain.com {
reverse_proxy app:3000
}
这就是全部配置。Caddy 会获取 Let’s Encrypt 证书并自动续签。
如何选择?
| Nginx + 手动证书 | Traefik | Caddy | |
|---|---|---|---|
| 配置难度 | 中等(配置 + 证书文件) | 中等(标签配置) | 极简 |
| 自动续签 | ❌(手动) | ✅ | ✅ |
| 灵活性 | 高 | 高 | 中等 |
| 学习曲线 | 低(熟悉) | 中等 | 低 |
| 最适合 | 小型部署,完全控制 | 动态容器,微服务 | 简单站点,最少配置 |
Docker 中的证书续签
方案一(Nginx + 手动):
# Replace cert files in your local ./certs/ directory
cp new-fullchain.pem ./certs/fullchain.pem
cp new-privkey.pem ./certs/privkey.pem
# Reload Nginx inside the container (no restart needed)
docker compose exec nginx nginx -s reload
方案二(Traefik)和方案三(Caddy):
自动处理——它们在内部管理续签。证书存储在 Docker 卷(traefik-acme 或 caddy-data)中,在容器重启后仍然保留。
Docker Swarm:使用 secrets
对于 Docker Swarm 部署,使用 Docker secrets 代替绑定挂载证书文件:
# Create secrets from your cert files
docker secret create ssl_cert fullchain.pem
docker secret create ssl_key privkey.pem
# In docker-compose.yml (deploy mode)
services:
nginx:
image: nginx:alpine
secrets:
- ssl_cert
- ssl_key
configs:
- source: nginx_conf
target: /etc/nginx/conf.d/default.conf
secrets:
ssl_cert:
external: true
ssl_key:
external: true
Secrets 在容器内挂载到 /run/secrets/ssl_cert 和 /run/secrets/ssl_key——静态和传输中都加密。
常见错误
将证书打包进 Docker 镜像
# ❌ NEVER do this
COPY fullchain.pem /etc/ssl/fullchain.pem
COPY privkey.pem /etc/ssl/privkey.pem
证书每 90 天过期一次。打包进镜像意味着每 60 天就要重新构建和重新部署。始终以卷或 secrets 方式挂载证书。
忘记暴露端口 80
端口 80 用于:
- HTTP → HTTPS 重定向
- Let’s Encrypt HTTP-01 验证(Traefik/Caddy 自动续签需要)
ports:
- "80:80" # Don't forget this
- "443:443"
未持久化 ACME 数据
Traefik 和 Caddy 将 Let’s Encrypt 证书和账户密钥存储在卷中。没有持久化,它们会在每次容器重启时重新请求证书——并触发速率限制:
volumes:
traefik-acme: # MUST be a named volume, not anonymous
caddy-data:
常见问题
应用容器可以直接处理 SSL 吗?
可以,但不推荐。反向代理模式(在边缘终结 TLS,内部转发明文 HTTP)是标准做法,因为:应用不需要知道证书的事情,续签不需要重启应用,而且你可以集中管理 TLS 配置。
如何为多个容器配置 SSL?
使用 Traefik 或 Caddy 时,为每个服务添加标签/配置——它们会自动获取各自的证书。使用 Nginx 时,在配置中添加多个 server 块。
应该用 GetHTTPS 还是 Traefik 内置的 ACME?
生产环境推荐使用 Traefik/Caddy 内置的 ACME——它自动处理续签。当你需要为 Nginx 反向代理方案(方案一)获取证书,或容器没有直接互联网访问无法完成 ACME 验证时,使用 GetHTTPS。
如何在 Docker 中安全地挂载证书?
以只读模式挂载(:ro),在 Swarm 模式下对私钥使用 Docker secrets,确保只有反向代理容器能访问证书文件。永远不要将证书打包进 Docker 镜像——它们会过期,你需要重新构建。
可以在 Kubernetes 中使用 GetHTTPS 证书吗?
可以。从 GetHTTPS 证书文件创建 Kubernetes TLS secret:
kubectl create secret tls my-tls-cert --cert=fullchain.pem --key=privkey.pem
然后在 Ingress 资源中引用它。要在 Kubernetes 中实现自动续签,可以考虑使用 cert-manager 配合 Let’s Encrypt ClusterIssuer。