容器与平台
Docker 中用 Certbot 为 Nginx 申请 Let's Encrypt 证书
Docker Compose 里的 Nginx 和官方 certbot/certbot 容器共享验证目录和证书卷:Certbot 用 webroot 方式为 example.com 申请 Let's Encrypt 证书,Nginx 直接读取证书,宿主机定时器负责续期和重载 Nginx。
适用于任意 Linux 宿主机上的 Docker Engine + Compose 插件(docker compose),Certbot 4.1 及以上镜像。
前提条件
Nginx 在宿主机上发布 80 和 443 端口,Certbot 只在需要时运行。
- 装有 Docker Engine 和 Compose 插件的 Linux 主机,有 sudo 权限。
example.com、www.example.com的 A/AAAA 记录指向主机,例如203.0.113.10;服务器在中国大陆的需已备案。- 防火墙和安全组放行 TCP 80、443,宿主机上没有其他程序占用这两个端口。
- 一个项目目录,例如
/opt/web。
第一步:创建 Compose 文件和目录
Nginx 和 Certbot 两个容器通过宿主机上的两个目录交换数据:certbot/www 放验证文件,certbot/conf 放证书。
sudo mkdir -p /opt/web/nginx/conf.d /opt/web/certbot/www /opt/web/certbot/conf
cd /opt/web
# /opt/web/compose.yaml
services:
nginx:
image: nginx:stable
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./certbot/www:/var/www/certbot:ro
- ./certbot/conf:/etc/letsencrypt:ro
restart: unless-stopped
certbot:
image: certbot/certbot
volumes:
- ./certbot/www:/var/www/certbot
- ./certbot/conf:/etc/letsencrypt
profiles: ["tools"]
tools profile 让 certbot 服务不随 docker compose up 启动,需要时手动运行。生产环境建议固定镜像标签,有计划地升级。
第二步:通过 HTTP 提供验证目录
Nginx 先只开 80 端口,把 /.well-known/acme-challenge/ 指向共享的验证目录:
# /opt/web/nginx/conf.d/example.com.conf
server {
listen 80;
server_name example.com www.example.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
cd /opt/web && sudo docker compose up -d nginx
第三步:申请证书
用一次性运行的 certbot 容器以 webroot 方式申请证书:
cd /opt/web
sudo docker compose run --rm certbot certonly --webroot -w /var/www/certbot \
--non-interactive --agree-tos \
-d example.com -d www.example.com
证书保存在宿主机的 ./certbot/conf/live/example.com/。Let's Encrypt 从 2025 年 6 月 4 日起不再发到期提醒邮件,不填邮箱也可以。
第四步:添加 HTTPS 配置
证书申请成功后,才能在同一个 Nginx 配置文件末尾追加 443 配置:
server {
listen 443 ssl;
http2 on; # nginx 1.25.1 及以上,当前 nginx:stable 镜像满足
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
root /usr/share/nginx/html;
}
}
cd /opt/web && sudo docker compose exec nginx nginx -t && sudo docker compose exec nginx nginx -s reload
要反代同一 Compose 网络里的其他容器,把 location / 换成 proxy_pass http://app:3000;。不要加 ssl_stapling,Let's Encrypt 证书没有 OCSP 地址。
验证
配置正确时,证书颁发者是 Let's Encrypt,HTTP 请求跳转到 HTTPS。用 SSL 证书检查 从外部检查,或在终端运行:
curl ssl.sh/example.com
在宿主机上:
cd /opt/web && sudo docker compose run --rm certbot certificates
curl -sI http://example.com | head -n 3
自动续期
Certbot 容器不常驻,续期交给宿主机的 systemd 定时器。certbot renew 会查询 ARI 窗口(Certbot 4.1+),到了续期时间才续期;没有续期时重载 Nginx 也没有副作用。
# /etc/systemd/system/certbot-docker.service
[Unit]
Description=Renew Let's Encrypt certificates (Docker)
[Service]
Type=oneshot
WorkingDirectory=/opt/web
ExecStart=/usr/bin/docker compose run --rm certbot renew --quiet
ExecStartPost=/usr/bin/docker compose exec -T nginx nginx -s reload
# /etc/systemd/system/certbot-docker.timer
[Unit]
Description=Twice-daily certificate renewal
[Timer]
OnCalendar=*-*-* 03,15:00:00
RandomizedDelaySec=1h
Persistent=true
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload && sudo systemctl enable --now certbot-docker.timer
cd /opt/web && sudo docker compose run --rm certbot renew --dry-run
2027 年 2 月 10 日 Let's Encrypt 默认证书改为 64 天后照常工作。Let's Encrypt 不再发提醒邮件,建议把域名加到 ssl.sh 监控(开发中)。
常见问题排查
- Nginx 启动报 cannot load certificate ... No such file or directory:证书还没申请就加了 443 配置,或只挂载了
live/,要挂载整个./certbot/conf。 - Timeout during connect 或验证返回 404:80 端口没发布、两个容器的验证目录路径不一致、解析不对或未备案被拦截,见 certbot 连接超时。
- too many certificates already issued:反复测试触发限制,调试时加
--test-cert,见 Let's Encrypt 频率限制。 - 续期后线上还是旧证书:重载步骤没执行,查看
systemctl status certbot-docker.service。 - App 或小程序报证书链不完整:Nginx 指向了
cert.pem,见 缺少中间证书。
常见问题
为什么不把 Certbot 装进 Nginx 容器?
服务器 80 端口不通,能在 Docker 里用 DNS 验证吗?
可以。Certbot 官方有 certbot/dns-cloudflare 等 DNS 插件镜像;域名在阿里云 DNS 或 DNSPod 的,更简单的做法是在宿主机上用 acme.sh 的 DNS API 签发,再把证书目录挂进 Nginx 容器。
国内拉取 Docker Hub 镜像失败怎么办?
可以在 /etc/docker/daemon.json 里配置云厂商提供的镜像加速地址(具体地址以阿里云、腾讯云容器镜像服务的说明为准),或者把 certbot/certbot 和 nginx 镜像同步到自己的私有镜像仓库后再拉取。