部署到服务器
上线前有三件事先定下来,后面所有选择都是它们的推论。
- 数据放在哪。 应用文件(
data/)和数据库是两处存储,备份与恢复都要成对处理。 - TLS 在哪终止。 推荐由反向代理终止,Lumo 只监听
127.0.0.1。 - 凭据谁管。 数据库连接串只走环境变量;SMTP 口令与对象存储密钥在后台填、加密入库。
方式一:二进制 + systemd
部署产物是单个静态二进制,不需要目标机器上有任何运行库。
sudo useradd --system --home /srv/lumo --shell /usr/sbin/nologin lumo
sudo install -d -o lumo -g lumo /srv/lumo /srv/lumo/data
sudo install -m 0755 -o lumo -g lumo lumo /srv/lumo/lumo
/etc/lumo.env(权限设成 0600,里面是数据库口令):
LUMO_DATA_DIR=/srv/lumo/data
LUMO_DATABASE_DSN=postgres://lumo:口令@127.0.0.1:5432/lumo?sslmode=disable
LUMO_SECURE_COOKIES=true
LUMO_TRUSTED_PROXIES=127.0.0.1/32
/etc/systemd/system/lumo.service:
[Unit]
Description=Lumo CMS
After=network-online.target postgresql.service
Wants=network-online.target
[Service]
Type=simple
User=lumo
Group=lumo
WorkingDirectory=/srv/lumo
EnvironmentFile=/etc/lumo.env
ExecStart=/srv/lumo/lumo serve
Restart=on-failure
RestartSec=5
# 停机时留出时间排空邮件队列与在途请求
TimeoutStopSec=40
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload && sudo systemctl enable --now lumo
sudo journalctl -u lumo -f
首次启动会在日志里打出安装向导地址;也可以在 /etc/lumo.env 里预先设好 DSN,跳过向导、改用 lumo admin create-user 建管理员。
方式二:Docker Compose
仓库的 deploy/ 下有整套文件:多阶段构建的 Dockerfile、带 PostgreSQL 17 的 docker-compose.yml 和 .env.example。
cd deploy
cp .env.example .env # 至少要填 POSTGRES_PASSWORD
docker compose up -d
docker compose logs -f lumo
几个刻意如此的默认值:
| 默认 | 为什么 |
|---|---|
端口只绑 127.0.0.1:8080 |
TLS 交给前面的反向代理;要直接对外就改 .env 里的 LUMO_BIND |
| PostgreSQL 不映射端口 | 它只需被同一 compose 网络内的应用访问 |
POSTGRES_PASSWORD 无默认值 |
没填就让 compose 当场报错,而不是用弱口令把库跑起来 |
LUMO_SECURE_COOKIES=false |
纯 HTTP 下开它会让登录「成功后立刻失效」;上了 HTTPS 必须改成 true |
LUMO_TRUSTED_PROXIES 为空 |
不采信任何 X-Forwarded-For,避免客户端伪造来源 IP |
两处容易踩的坑:
- 数据库口令含特殊字符(
@ : / ? # %等)时要分开填两个变量:POSTGRES_PASSWORD保持原始口令,LUMO_DATABASE_DSN单独填只对口令段做百分号编码的完整 URL。把编码后的串填进POSTGRES_PASSWORD会让首次部署必然失败。 - 镜像里没有 shell(distroless),排障只能靠
docker compose logs。容器内也无法做 HEALTHCHECK,探活从外部请求/healthz(进程存活)或/readyz(额外探测数据库,可用于负载均衡摘流)。
反向代理与 HTTPS
Lumo 用一次性 cookie 与 SameSite=Lax,在 HTTPS 后面需要两项配置:
LUMO_SECURE_COOKIES=true # 会话 Cookie 加 Secure 属性,并启用 __Host- 前缀
LUMO_TRUSTED_PROXIES=127.0.0.1/32 # 可信反代的 CIDR
LUMO_SECURE_COOKIES 不只是加个 Secure 属性——它会连带把 cookie 名换成 __Host- 前缀,防的是同域下别的子域往你这里写会话 cookie。站点在某个域名的子域上时,这条是真在防东西。
开着它时改动的效果是现有的浏览器登录全部失效(cookie 名变了),重新登录一次即可;API 令牌不受影响。
LUMO_TRUSTED_PROXIES 不配的话,程序在日志里会提示「未配置可信代理,将忽略 X-Forwarded-For 并使用直连地址」——所有访客在它眼里是同一个来源 IP,评论的 IP 限流会退化成全站共用一个额度。填反代的来源地址:反代和 Lumo 在同一台机器上就是 127.0.0.1/32,反代在容器里则是 docker 网关地址。
nginx 侧最小配置:
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 64m; # 不小于 Lumo 的 multipart 上传上限
}
client_max_body_size 别忘。 Lumo 的普通请求与 multipart 上传是两条独立上限(默认 10 MiB / 64 MiB),代理层卡住会比应用层先报 413,现象是「小文件能传、大文件 413」。
站点地址要在后台「设置 → 站点信息」里填成最终对外的地址(含 https://),canonical、sitemap.xml 与 feed.xml 里的绝对链接都取自它。
备份
数据库、data/ 目录、以及环境变量文件三样都要备。 密钥只存在于 data/secret.key 里,库和卷里都没有;/etc/lumo.env 或 deploy/.env 含数据库口令,必须单独备份。
要让库与文件落在同一时点,先停应用再备:
sudo systemctl stop lumo
pg_dump -Fc lumo > lumo-db-$(date +%Y%m%d-%H%M%S).dump
tar czf lumodata-$(date +%Y%m%d-%H%M%S).tar.gz -C /srv/lumo/data uploads themes plugins secret.key install.json
sudo systemctl start lumo
备份里含口令哈希、会话与令牌的哈希,按密钥对待:不要放公开目录,传输与长期保存都要加密。
附件存在对象存储里时,上传文件都不在 data/ 下,只备卷等于没备附件——给桶开版本控制并设足够的保留期,或定期导出。恢复时桶内容必须与数据库 dump 对应同一时点。
恢复
先停应用。删库重建,而不是只加 --clean——--clean 清不掉上一版新建的表。
sudo systemctl stop lumo
dropdb --force lumo && createdb -O lumo lumo
pg_restore -d lumo lumo-db.dump
# 卷:清掉旧内容再解开归档
rm -rf /srv/lumo/data/{uploads,themes,plugins}
tar xzf lumodata.tar.gz -C /srv/lumo/data
sudo systemctl start lumo
Docker 部署时把属主改回 65532:65532(镜像里的 nonroot),否则文件写不进去。
恢复后逐项检查:/healthz 与 /readyz 返回 200;日志里没有迁移报错;原账号能登录;抽一篇已发布文章前台渲染正常;附件可访问、媒体库能列出;评论正常显示。
升级与回滚
先把镜像或二进制钉到具体版本,备份三样之后再动手。
sudo systemctl stop lumo
sudo install -m 0755 -o lumo -g lumo lumo-new /srv/lumo/lumo
sudo systemctl start lumo # 启动时自动执行迁移
迁移只向前。 新版本还没跑过迁移时,换回旧二进制即可回滚;一旦跑了迁移,就必须恢复升级前的数据库 dump 与 data/ 归档,再切回旧版本(顺序:停 → 换版本 → 恢复库 → 恢复文件 → 起)。
Docker 部署同理:docker compose pull lumo && docker compose up -d lumo;回滚要把 compose 里的 image: 钉回具体版本或摘要。
排障
| 现象 | 先看什么 |
|---|---|
| 启动即退出 | 日志里会打出首个错误;install.json 不存在时会转入安装模式,不是故障 |
| 登录成功但立刻失效 | 纯 HTTP 下 LUMO_SECURE_COOKIES=true 的表现,或 HTTPS 下反代没传 X-Forwarded-Proto |
| 大文件上传 413 | 反代的 client_max_body_size 小于 Lumo 的上传上限 |
| 访客 IP 全一样 | 没配 LUMO_TRUSTED_PROXIES |
/readyz 返回 503 |
数据库连不上,看 DSN 与网络;/healthz 只探测进程存活 |
| 前台样式丢失 | 反向代理没放行 /theme-assets/** 与 /uploads/** |
| 后台打开是构建提示 | 二进制构建前没有生成前端产物,task all 而不是 task build |
评论
还没有评论,来说两句。