单机服务器上使用 Caddy + Docker Compose 实现 HTTPS 自动证书部署
·
在单机服务器上部署 Web 应用时,如果希望:
- 自动申请 HTTPS 证书
- 自动续期
- 不让业务容器暴露公网端口
- 架构清晰、可扩展
使用 Caddy 作为统一入口是一个非常合适的选择。
本文记录一种常见且可长期演进的部署结构。
一、整体架构结构

单机结构如下:
公网
↓
[ Caddy : 80 / 443 ]
↓
[ Docker 网络 ]
├── app
├── mysql
├── redis
核心原则:
- 只有 Caddy 暴露 80 和 443
- 业务容器只在 Docker 内网通信
- HTTPS 证书由 Caddy 自动申请与续期
二、为什么必须开放 80 和 443?
Let’s Encrypt 通过 ACME 协议验证域名所有权。
默认验证方式:
| 验证方式 | 是否需要公网端口 | 说明 |
|---|---|---|
| HTTP-01 | 需要 80 | 访问 .well-known路径验证 |
| TLS-ALPN-01 | 需要 443 | 通过 TLS 层验证 |
| DNS-01 | 不需要 | 通过 DNS TXT 验证 |
Caddy 默认使用 HTTP-01 或 TLS-ALPN-01,因此:
服务器必须:
- 有公网 IP
- 域名解析到该 IP
- 开放 80 / 443
否则证书签发会失败。
三、Docker Compose 部署方式
1️⃣ docker-compose.yml
version: "3.9"
services:
caddy:
image: caddy:2
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
depends_on:
- app
app:
image: your-app-image
container_name: app
restart: unless-stopped
expose:
- "3000"
volumes:
caddy_data:
caddy_config:
说明:
ports只在 Caddy 上声明expose只暴露给 Docker 内网/data用于存储证书(必须持久化)
2️⃣ Caddyfile
yourdomain.com {
reverse_proxy app:3000
}
启动:
docker compose up -d
首次启动时:
- 自动申请证书
- 自动启用 HTTPS
- 自动 HTTP → HTTPS 跳转
- 自动续期
四、不建议的方式
直接暴露业务容器端口
例如:
docker run -p 3000:3000 your-app
问题:
- 无法自动签发 HTTPS
- IP 不能申请公开证书
- 证书管理复杂
- 架构无法扩展
生产环境通常将入口层与业务层分离。
五、DNS-01 模式(可选)
如果服务器无法开放 80/443,可以使用 DNS-01。
例如使用 Cloudflare DNS。
这种方式:
- 不需要开放端口
- 适合内网部署
- 需要构建带 DNS 插件的 Caddy
复杂度高于默认模式,适合特殊场景。
六、单机可扩展结构
当需要蓝绿发布或多服务时,结构可以演进为:
服务器
├── Caddy(80/443)
├── app-blue
├── app-green
├── mysql
├── redis
Caddy 只负责:
- HTTPS
- 反向代理
- 流量切换
- 域名管理
业务容器专注应用逻辑。
七、安全建议
生产环境建议:
开放端口:
22 (SSH)
80 (ACME + 重定向)
443 (HTTPS)
关闭其他公网端口
Docker 容器之间使用 bridge 网络通信。
八、小结
单机部署时:
- 使用 Caddy 作为唯一公网入口
- 使用 Docker Compose 管理容器
- 业务容器不暴露公网端口
- 自动证书与续期交给 Caddy
这种结构足够支撑:
- 多站点
- 蓝绿发布
- 多租户系统
- 平滑扩展到多节点架构
更多推荐

所有评论(0)