Caddy自动化HTTPS部署:Ubuntu 18.04生产级实战指南

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 生命周期管理器:
    1. 预检 :检查域名 DNS 解析是否指向本机 IP;
    2. 挑战 :向 Let’s Encrypt 发起 HTTP-01 挑战,将临时 token 写入 .well-known/acme-challenge/ 目录;
    3. 验证 :Let’s Encrypt 的验证服务器访问 http://example.com/.well-known/acme-challenge/token ,确认控制权;
    4. 签发 :获取证书和私钥,保存在 /var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/
    5. 续期 :证书到期前 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 真正赋予你的力量——不是更快,而是更稳;不是更炫,而是更省心。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值