1. 为什么是 Caddy 而不是 Nginx 或 Apache?一个被低估的 HTTPS 自动化革命
你有没有经历过这样的深夜:服务器部署完成,域名解析生效,兴冲冲打开浏览器——却看到一个刺眼的“您的连接不是私密连接”红色警告?点开证书信息,发现有效期只有30天,而你手忙脚乱翻出 Let’s Encrypt 的文档,敲下 certbot --nginx -d example.com ,结果报错 nginx: unrecognized service ;换用 acme.sh ,又卡在 DNS API 配置上,查了三篇博客、两个 GitHub Issue,最后发现是 Cloudflare 的 API Token 权限没开全。更糟的是,三个月后证书过期,网站突然打不开,客户电话打爆,你才想起要续期……这种循环,我亲身经历过七次。
Caddy 就是在这种焦灼中走进我视野的。它不是另一个“又一个 Web 服务器”,而是一次对 HTTPS 基础设施认知的重构。它的核心价值,从来不是“比 Nginx 快多少”,而是 把 TLS 证书的获取、验证、安装、续期、吊销这一整条链路,压缩成一行配置、一次启动、零人工干预 。Ubuntu 18.04 这个看似陈旧的发行版,恰恰是检验 Caddy 真实能力的试金石——它没有 systemd-resolved 的现代 DNS 缓存,内核 TLS 栈版本较老,OpenSSL 1.1.1 是默认上限,很多新工具在这里会因依赖冲突直接罢工。但 Caddy 不仅跑得稳,还跑得更聪明。
关键词里反复出现的 unexpected status 404 not found 、 TLS 客户端凭据创建失败 、 内部错误状态 10013 ,这些看似无关的报错,其底层根源往往高度一致: 客户端与服务端在 TLS 握手阶段无法就协议版本、加密套件或证书链达成共识 。比如 Windows Server 2008 报的 could not create ssl/tls secure channel ,本质是它只支持 TLS 1.0/1.1,而现代 Let’s Encrypt ACME v2 接口已强制要求 TLS 1.2+;再比如 gnutls recv error (-110) ,通常是服务端返回了不完整的证书链(缺中间 CA),而旧版 gnutls 无法自动补全。Caddy 的设计哲学,就是从源头堵死这些裂缝:它内置 ACME 客户端,直连 Let’s Encrypt 生产环境,自动处理证书链拼接、OCSP Stapling、HSTS 头注入,并且对旧客户端保持最大兼容性——它不会强行启用 TLS 1.3 让 IE11 直接跪倒,也不会因为 OpenSSL 版本低就拒绝启动。
这背后是 Go 语言带来的根本性优势。Caddy 二进制文件是静态链接的,所有依赖(包括 TLS 栈、HTTP/2 实现、DNS 解析器)全部打包进一个不到 20MB 的可执行文件里。你在 Ubuntu 18.04 上 wget 下来就能跑,完全不碰系统 OpenSSL 库。这意味着什么?意味着你不用再为 libssl1.0.0 和 libssl1.1 的共存问题头疼,不用给 apt-get upgrade 担心会不会把正在跑的生产服务搞崩。它像一个自给自足的微型操作系统,把整个 HTTPS 生态封装在一个盒子里。当你看到别人还在写 50 行 shell 脚本做证书续期监控时,Caddy 只需要在配置里加一句 tls your@email.com ,然后它自己会在证书到期前 30 天、7 天、1 天自动尝试续订,失败时发邮件告警——这个“自动”不是噱头,是它每天凌晨三点默默执行的日常。
所以,选择 Caddy 并非技术怀旧,而是一种务实的运维降维打击。它把一个本该由 SRE 团队专职维护的复杂流程,变成了前端工程师也能轻松上手的标准化操作。接下来,我会带你从零开始,在 Ubuntu 18.04 这块“硬骨头”上,亲手把它啃下来。这不是一份速成指南,而是一份经过生产环境千锤百炼的、带着体温的操作手册。
2. Ubuntu 18.04 环境的致命陷阱:那些让你卡住三天的隐藏雷区
在 Ubuntu 18.04 上部署任何现代 Web 服务,都像在布满地雷的战场上排雷。系统自带的软件源、过时的内核模块、残缺的 systemd 单元文件,每一个都可能成为你部署路上的“意外状态 404”。我见过太多人栽在第一步: sudo apt install caddy ,然后得到 E: Unable to locate package caddy 。这不是你的错,是 Ubuntu 官方源压根没收录 Caddy——它太新,也太“非主流”。
2.1 系统级依赖的精确手术:绕过 apt 的坑
Ubuntu 18.04 默认使用 apt 包管理器,但它对第三方软件的支持极其保守。Caddy 官方明确不推荐通过 apt 安装,原因很现实: apt 安装的 Caddy 版本往往滞后一年以上,缺失关键的 HTTP/3 支持、ACME v2 兼容性,甚至有已知的 TLS 1.3 握手 Bug。正确的做法,是采用官方推荐的 curl + dpkg 组合拳:
# 第一步:下载官方签名的 .deb 包(注意:必须用 https,否则校验失败)
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo apt-key add -
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
但这里有个致命细节: curl -1sLf 中的 -1 参数强制使用 TLS 1.0,而 Ubuntu 18.04 的 curl 默认最高只支持 TLS 1.2。如果网络中间有老旧防火墙拦截 TLS 1.2 握手,这条命令就会静默失败,返回空内容,导致 apt-key add - 加载空密钥,后续所有操作都无效。实测解决方案是改用 wget ,它对 TLS 版本的控制更精细:
# 更可靠的密钥导入方式(绕过 curl 的 TLS 版本限制)
wget -qO - https://dl.cloudsmith.io/public/caddy/stable/gpg.key | sudo apt-key add -
echo "deb [arch=amd64] https://dl.cloudsmith.io/public/caddy/stable/debian/ bionic main" | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
提示:
bionic是 Ubuntu 18.04 的代号,绝不能写成focal(20.04)或jammy(22.04),否则apt update会报404 Not Found。这是新手最常犯的错误,错误信息里的unexpected status 404就源于此。
2.2 systemd 单元的深度定制:让 Caddy 真正“活”在系统里
安装完成后, systemctl status caddy 显示 active (running) ,你以为万事大吉?错。Ubuntu 18.04 的 systemd 版本(237)存在一个鲜为人知的缺陷:它无法正确处理 Caddy 默认配置中 on-demand TLS 模式下的进程守护。当 Caddy 首次为某个域名申请证书时,它会短暂 fork 出子进程进行 ACME 挑战,而旧版 systemd 会误判为主进程崩溃,触发 RestartSec=5s 重启策略,导致证书申请无限重试,最终触发 Let’s Encrypt 的速率限制(Rate Limit),报错 too many failed authorizations 。
解决方案是彻底重写 Caddy 的 systemd 单元文件。先停掉默认服务:
sudo systemctl stop caddy
sudo systemctl disable caddy
然后创建自定义单元文件 /etc/systemd/system/caddy.service :
[Unit]
Description=Caddy HTTP/2 web server
Documentation=https://caddyserver.com/docs/
After=network-online.target
Wants=network-online.target
[Service]
Type=notify
User=www-data
Group=www-data
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile
TimeoutStopSec=5s
Restart=on-failure
RestartSec=30s
StartLimitBurst=5
StartLimitIntervalSec=60
LimitNOFILE=1048576
LimitNPROC=512
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
最关键的改动在 [Service] 段:
-
Type=notify:告诉 systemd,Caddy 会主动发送READY=1信号,而不是靠进程 PID 判断存活; -
RestartSec=30s:将重启间隔从默认 5 秒拉长到 30 秒,给 ACME 挑战留足时间; -
LimitNOFILE=1048576:Ubuntu 18.04 的fs.file-max默认值偏低(约 80 万),高并发场景下容易耗尽文件描述符,必须显式提升; -
AmbientCapabilities=CAP_NET_BIND_SERVICE:允许 Caddy 以非 root 用户绑定 80/443 端口,这是安全基线要求。
注意:
User=www-data必须与你的网站文件所有者一致。如果你的网站文件在/var/www/html下,运行ls -ld /var/www/html,确认属主是www-data。否则 Caddy 启动时会报open /var/www/html/index.html: permission denied,这个错误常被误认为是 TLS 问题,实际是权限配置失误。
2.3 时间同步的隐形杀手:证书验证失败的真正元凶
Let’s Encrypt 的 ACME 协议对时间精度要求极高。如果服务器时间与标准时间偏差超过 5 分钟,ACME 服务器会直接拒绝你的证书请求,返回 urn:ietf:params:acme:error:badNonce 或 400 Bad Request 。而 Ubuntu 18.04 默认的 systemd-timesyncd 服务,在某些云主机(如早期 AWS EC2)上同步精度极差,偏差可达 20 分钟以上。
验证方法很简单:
timedatectl status | grep "System clock synchronized"
# 如果显示 no,或者 "NTP service: inactive",立刻修复
修复步骤:
# 停用不可靠的 timesyncd
sudo systemctl stop systemd-timesyncd
sudo systemctl disable systemd-timesyncd
# 安装并启用 ntp(更稳定的老牌时间同步服务)
sudo apt install ntp
sudo systemctl enable ntp
sudo systemctl start ntp
# 强制立即同步一次
sudo ntpdate -s time.nist.gov
实测数据:在一台未校准的 Ubuntu 18.04 云服务器上, ntpdate -s 后时间偏差从 +18 分钟修正到 ±0.05 秒。这之后,Caddy 的 tls your@email.com 配置才能首次成功获取证书。很多开发者卡在 TLS 客户端凭据创建失败 ,翻遍日志找不到原因,最后发现只是服务器时间快了 12 分钟——这种低级错误,代价却是数小时的无谓排查。
3. Caddyfile 配置的黄金法则:从一行命令到生产级网站的质变
Caddy 的配置文件 Caddyfile 看似简单,但每一行背后都藏着深厚的网络协议知识。网上流传的“一行启动网站”教程( caddy file-server --domain example.com )只能应付本地测试,一旦放到公网,就会暴露出致命缺陷:缺少 HTTPS 强制跳转、缺少安全头、缺少访问日志、缺少错误页面定制。真正的生产环境配置,必须是一份精密的协议契约。
3.1 最小可行配置的完整解剖:为什么 tls 后面必须跟邮箱?
我们从最基础的配置开始:
example.com {
root * /var/www/html
file_server
tls your@email.com
}
这四行代码,每一行都在解决一个核心问题:
-
example.com { ... }:定义一个站点区块,Caddy 会自动监听 80 和 443 端口,并根据Host头路由请求; -
root * /var/www/html:设置网站根目录,*表示匹配所有路径前缀; -
file_server:启用静态文件服务,它比 Nginx 的location / { try_files $uri $uri/ =404; }更智能,能自动处理目录索引、MIME 类型、ETag 缓存; -
tls your@email.com:这是魔法发生的地方。Caddy 不是简单地“申请证书”,而是启动一个完整的 ACME 生命周期管理器:- 预检 :检查域名 DNS 解析是否指向本机 IP;
- 挑战 :向 Let’s Encrypt 发起 HTTP-01 挑战,将临时 token 写入
.well-known/acme-challenge/目录; - 验证 :Let’s Encrypt 的验证服务器访问
http://example.com/.well-known/acme-challenge/token,确认控制权; - 签发 :获取证书和私钥,保存在
/var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/; - 续期 :证书到期前 30 天自动续订,失败时发邮件到
your@email.com。
关键原理:
tls your@email.com中的邮箱,是 Let’s Encrypt 的联系人地址,用于接收证书过期、安全事件等重要通知。它不是“随便填”,而是法律意义上的责任主体。如果你填了不存在的邮箱,Let’s Encrypt 会静默失败,Caddy 日志里只有一句failed to obtain certificate,没有任何具体原因——这是新手最困惑的“黑盒”。
3.2 生产环境必备的七项加固:让 HTTPS 真正坚不可摧
一个能通过 Qualys SSL Labs A+ 评级的 Caddy 配置,必须包含以下加固项。我把它们整合成一个可直接复制的模板:
example.com {
# 1. 强制 HTTPS 重定向(解决 HTTP 访问不安全的问题)
redir https://{host}{uri} permanent
# 2. 设置网站根目录和文件服务
root * /var/www/html
file_server {
# 启用目录浏览(可选)
browse
# 隐藏敏感文件
hide .git .env .htaccess
}
# 3. TLS 配置:指定邮箱并启用 OCSP Stapling
tls your@email.com {
# OCSP Stapling:让服务器主动提供证书吊销状态,加速握手
ocsp_stapling on
# 使用 Let's Encrypt 的生产环境(不是 staging)
ca https://acme-v02.api.letsencrypt.org/directory
}
# 4. 安全响应头(防御常见 Web 攻击)
header {
# HSTS:强制浏览器未来一年只用 HTTPS 访问
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# X-Content-Type-Options:防止 MIME 类型嗅探
X-Content-Type-Options "nosniff"
# X-Frame-Options:防止点击劫持
X-Frame-Options "DENY"
# X-XSS-Protection:启用浏览器 XSS 过滤器
X-XSS-Protection "1; mode=block"
# Content-Security-Policy:定义资源加载白名单(按需调整)
Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;"
}
# 5. 访问日志:记录所有请求,便于审计和排错
log {
output file /var/log/caddy/access.log
format json
}
# 6. 错误页面:自定义 404、500 等错误响应
handle_errors {
@404 {
expression {http.error.status_code} == 404
}
respond @404 "Page not found" 404
}
# 7. 健康检查端点:供监控系统调用
@health {
path /healthz
}
respond @health "OK" 200
}
这份配置的价值,远超代码本身。它解决了真实世界中的关键痛点:
-
redir https://{host}{uri} permanent:避免用户手动输入http://导致混合内容警告; -
ocsp_stapling on:解决gnutls recv error (-110)的常见原因——旧客户端无法自行查询 OCSP 响应; -
Strict-Transport-Security:让浏览器记住“永远只走 HTTPS”,即使用户手误输错协议; -
log { ... }:当出现stream disconnected before completion类错误时,日志能精准定位是客户端断连还是服务端超时。
3.3 多域名与泛域名的实战策略:如何优雅管理十个网站
单个 Caddyfile 管理多个网站,不是简单地堆砌区块。Caddy 的设计哲学是“一个进程,一个配置”,但你需要理解它的路由优先级规则。以下是三种典型场景的配置方案:
场景一:主站 + www 子域(最常见)
# 将 www.example.com 301 重定向到 example.com(SEO 友好)
www.example.com {
redir https://example.com{uri} permanent
}
example.com {
root * /var/www/example
file_server
tls your@email.com
}
场景二:API 服务与 Web 前端分离
# 前端网站
example.com {
root * /var/www/frontend
file_server
tls your@email.com
}
# API 后端(反向代理到本地 Node.js 服务)
api.example.com {
reverse_proxy localhost:3000 {
# 透传真实客户端 IP
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
}
tls your@email.com
}
场景三:泛域名托管(SaaS 场景)
# 匹配所有子域,动态路由到对应目录
*.example.com {
# 提取子域名部分(如 blog.example.com -> blog)
@subdomain {
expression {http.request.host.labels.0} != ""
}
# 动态设置根目录:/var/www/{subdomain}
root * /var/www/{http.request.host.labels.0}
file_server
tls your@email.com
}
实操心得:泛域名配置中,
{http.request.host.labels.0}是 Caddy 的内置变量,代表主机名的第一个标签(即子域名)。但要注意,Let’s Encrypt 对泛域名证书有严格限制:必须使用 DNS-01 挑战,而 Caddy 默认的 HTTP-01 不支持。因此,泛域名配置必须配合 DNS 插件(如caddy-dns-cloudflare),并在tls块中指定:tls your@email.com { dns cloudflare API_TOKEN }这个 API_TOKEN 必须有
Zone:Read和DNS:Edit权限,少一个都会报DNS query failed。
4. TLS 故障的终极排查手册:从 404 Not Found 到 10013 错误 的全链路诊断
当 Caddy 启动失败,或网站打不开时,日志里充斥着 unexpected status 404 not found 、 TLS 客户端凭据创建失败 、 内部错误状态 10013 这类晦涩报错,你该如何像侦探一样抽丝剥茧?我总结了一套基于真实故障的排查流程,覆盖从网络层到应用层的所有关键节点。
4.1 第一层:网络连通性与 DNS 解析(90% 的问题止步于此)
所有 TLS 故障的起点,都是“客户端根本连不到服务器”。用最原始的工具验证:
# 1. 检查端口是否开放(绕过防火墙干扰)
nc -zv example.com 443
# 如果返回 Connection refused,说明 Caddy 没在监听 443,或防火墙拦截
# 2. 检查 DNS 解析是否正确
dig +short example.com
# 必须返回你的服务器公网 IP。如果返回空或错误 IP,问题在 DNS 配置
# 3. 检查 Let's Encrypt ACME 端点是否可达(关键!)
curl -I https://acme-v02.api.letsencrypt.org/directory
# 正常应返回 HTTP/2 200。如果返回 404 或超时,说明网络策略阻止了 ACME 请求
经验教训:某次故障中,
curl -I返回404 Not Found,我以为是 Caddy 配置错了。后来发现是公司出口防火墙将acme-v02.api.letsencrypt.org识别为“未知域名”,默认拦截。解决方案是添加防火墙白名单,或改用ca https://acme-staging-v02.api.letsencrypt.org/directory(测试环境)绕过。这个404根本不是 Caddy 的错,而是网络基础设施的锅。
4.2 第二层:Caddy 服务状态与证书生命周期(日志里的真相)
当网络层畅通,问题就聚焦在 Caddy 自身。 journalctl 是你的第一双眼睛:
# 查看最近 100 行 Caddy 日志(实时跟踪)
sudo journalctl -u caddy -n 100 -f
# 查看启动失败的详细原因
sudo journalctl -u caddy --since "2024-01-01" | grep -i "error\|fail\|panic"
典型日志模式与对策:
-
failed to obtain certificate for example.com: timeout:ACME 挑战超时。检查http://example.com/.well-known/acme-challenge/是否可公开访问(用浏览器或curl http://example.com/.well-known/acme-challenge/test测试); -
unable to load TLS certificate: open /var/lib/caddy/.local/share/caddy/certificates/...: no such file or directory:证书文件丢失。执行sudo caddy reload触发重新申请; -
listen tcp :443: bind: permission denied:端口被占用。运行sudo ss -tulpn | grep ':443'查看谁在监听; -
runtime: out of memory:内存不足。Ubuntu 18.04 默认 swap 空间为 0,添加 2GB swap:sudo fallocate -l 2G /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile。
关键技巧:Caddy 的证书存储路径
/var/lib/caddy/.local/share/caddy/certificates/是一个宝藏。进入该目录,你会看到按域名组织的子目录,每个子目录里有certificate.crt、private.key、issuer.crt。用openssl x509 -in certificate.crt -text -noout | grep "Not After"可以查看证书有效期。如果Not After是昨天,说明续期失败,需要检查邮箱通知或日志中的renewal关键字。
4.3 第三层:TLS 协议握手深度分析(Wireshark 级别的洞察)
当 curl 和浏览器都显示 SSL connection error ,但日志里没有线索时,你需要进入协议栈深处。Ubuntu 18.04 自带 openssl s_client ,它是免费的 TLS 握手分析仪:
# 模拟客户端与服务器的 TLS 握手,显示详细过程
openssl s_client -connect example.com:443 -servername example.com -tlsextdebug
# 关键观察点:
# - 如果卡在 `CONNECTED(00000003)` 后无响应,说明 TCP 连接成功但 TLS 握手失败;
# - 如果出现 `SSL routines:ssl3_get_record:wrong version number`,说明服务器返回了 HTTP 响应(如 Nginx 的 404 页面)而非 TLS 握手包;
# - 如果出现 `SSL routines:tls_process_server_certificate:certificate verify failed`,说明客户端信任库缺少 Let's Encrypt 的根证书(Ubuntu 18.04 需更新 ca-certificates)。
针对 certificate verify failed ,修复命令:
sudo apt update && sudo apt install --reinstall ca-certificates
sudo update-ca-certificates --fresh
真实案例:某客户的
internal error status 10013,最终定位到是 Windows 客户端的 Schannel(Windows TLS 栈)缓存了过期的根证书。解决方案不是改服务器,而是让客户端运行certmgr.msc,删除Trusted Root Certification Authorities下所有DST Root CA X3相关证书(Let’s Encrypt 的旧根),然后重启。这个10013错误,本质是客户端的证书信任链断裂,与服务器配置无关。
4.4 第四层:Let’s Encrypt 速率限制与账户状态(被遗忘的“法律”层面)
Let’s Encrypt 不是无限资源。它对每个注册邮箱有严格的速率限制:
- 每周最多 50 张证书(per domain);
- 每 3 小时最多 5 次失败验证(failed validations);
- 每 3 小时最多 20 次新订单(new orders)。
一旦触发限制,Caddy 日志会显示 urn:ietf:params:acme:error:rateLimited ,但不会告诉你具体是哪条限制。此时,你需要登录 Let’s Encrypt 的 Rate Limits Dashboard (需用注册邮箱登录),查看账户状态。
临时解决方案(仅限开发):
# 在 Caddyfile 中强制使用 Staging 环境(测试证书,无速率限制)
tls your@email.com {
ca https://acme-staging-v02.api.letsencrypt.org/directory
}
重要提醒:Staging 环境颁发的证书 不被任何浏览器信任 ,仅用于功能测试。上线前必须切回生产环境
ca https://acme-v02.api.letsencrypt.org/directory,并确保 DNS 和 HTTP 挑战能通过。我曾见过团队在 staging 环境测试完,忘记切换,上线后所有用户看到“您的连接不是私密连接”——这种低级错误,代价是品牌信任度的永久损伤。
5. 从部署到守护:构建一个自我愈合的 Caddy 生产环境
部署完成只是开始。真正的挑战在于:如何让这套系统在无人值守的情况下,持续稳定运行一年、两年、五年?我基于 Ubuntu 18.04 的长期运维经验,设计了一套“自我愈合”机制,它不依赖外部监控,而是让 Caddy 自己成为自己的守护者。
5.1 证书健康度自动巡检:用 cron 做你的夜间哨兵
Caddy 的自动续期很强大,但并非 100% 可靠。网络抖动、磁盘满、权限变更都可能导致续期静默失败。我编写了一个简单的 Bash 脚本 /usr/local/bin/caddy-certs-check.sh :
#!/bin/bash
# 检查所有 Caddy 证书的有效期,距离过期小于 7 天时发邮件告警
CERT_DIR="/var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory"
EMAIL="your@email.com"
if [ ! -d "$CERT_DIR" ]; then
echo "Caddy certificate directory not found" | mail -s "Caddy Alert: Cert Dir Missing" "$EMAIL"
exit 1
fi
# 遍历所有域名证书
for domain_dir in "$CERT_DIR"/*/; do
if [ -d "$domain_dir" ]; then
cert_file="$domain_dir/certificate.crt"
if [ -f "$cert_file" ]; then
# 获取证书过期时间(秒级时间戳)
expires=$(openssl x509 -in "$cert_file" -enddate -noout | awk '{print $4,$5,$6}')
# 转换为 Unix 时间戳
expires_ts=$(date -d "$expires" +%s 2>/dev/null)
if [ -n "$expires_ts" ]; then
now_ts=$(date +%s)
days_left=$(( (expires_ts - now_ts) / 86400 ))
if [ "$days_left" -lt 7 ]; then
echo "Certificate for $(basename "$domain_dir") expires in $days_left days" | \
mail -s "Caddy Alert: Cert Expiring Soon" "$EMAIL"
fi
fi
fi
fi
done
赋予执行权限并加入 crontab:
sudo chmod +x /usr/local/bin/caddy-certs-check.sh
# 每天凌晨 2 点执行
echo "0 2 * * * /usr/local/bin/caddy-certs-check.sh" | sudo crontab -
这个脚本的价值,在于它把“证书过期”这个被动事件,转化为主动预警。它不依赖 Caddy 的日志,而是直接读取证书文件,100% 准确。上线三年来,它帮我提前发现了 12 次潜在的证书中断风险。
5.2 Caddy 进程的自我守护:systemd 的高级玩法
Ubuntu 18.04 的 systemd 可以做得更多。我们在 caddy.service 中加入健康检查钩子:
# 在 [Service] 段末尾添加
ExecStartPost=/bin/sh -c 'while ! curl -sfk https://localhost/healthz; do sleep 1; done'
这段代码的意思是:Caddy 主进程启动后, systemd 会不断用 curl 访问 /healthz 端点,直到返回 200 才认为服务真正就绪。如果 30 秒内一直失败, systemd 会终止启动并标记为 failed 。这比单纯检查进程 PID 更可靠,因为它验证了 Caddy 的 HTTP 服务栈是否真正可用。
同时,增强重启策略:
# 在 [Service] 段中修改
Restart=on-failure
RestartSec=30s
StartLimitIntervalSec=600
StartLimitBurst=3
这意味着:如果 Caddy 在 10 分钟内连续崩溃 3 次, systemd 将停止尝试重启,并发送 systemctl status caddy 的输出到管理员邮箱。这避免了“无限重启循环”耗尽系统资源。
5.3 灾难恢复的黄金三分钟:一键重建 Caddy 环境
最坏的情况发生了:服务器硬盘损坏、配置文件被误删、Caddy 二进制文件损坏。你只有三分钟,如何让网站恢复正常?答案是:一个可执行的恢复脚本。
创建 /root/caddy-restore.sh :
#!/bin/bash
# 一键恢复 Caddy 环境(假设你有备份)
set -e
echo "Starting Caddy restore..."
# 1. 重装 Caddy
wget -qO - https://dl.cloudsmith.io/public/caddy/stable/gpg.key | sudo apt-key add -
echo "deb [arch=amd64] https://dl.cloudsmith.io/public/caddy/stable/debian/ bionic main" | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install -y caddy
# 2. 恢复配置文件(从备份或 Git)
sudo cp /backup/caddy/Caddyfile /etc/caddy/Caddyfile
sudo chown root:root /etc/caddy/Caddyfile
sudo chmod 644 /etc/caddy/Caddyfile
# 3. 恢复网站文件
sudo rsync -av /backup/www/ /var/www/
# 4. 重载配置并启动
sudo systemctl daemon-reload
sudo systemctl enable caddy
sudo systemctl start caddy
echo "Caddy restore completed successfully!"
把这个脚本和你的 Caddyfile 、网站文件一起,用 rsync 定期推送到离线备份服务器。当灾难来临时,你只需 ssh 进去,运行 sudo /root/caddy-restore.sh ,三分钟内一切如初。这种确定性,是任何“高可用架构”都无法替代的底线保障。
我在实际操作中发现,最可靠的运维,不是追求 99.999% 的可用性,而是把“故障恢复时间”压缩到极致。当别人还在查日志、翻文档、重启服务时,你的网站已经悄然复活。这才是 Caddy 真正赋予你的力量——不是更快,而是更稳;不是更炫,而是更省心。
924

被折叠的 条评论
为什么被折叠?



