从零把博客重新搭起来:Mix Space、Yohaku 和一点点回滚经验
风停下来的地方,也需要有人先把屋檐搭好。
这篇算是一份回头看的部署记录:从后端、前端、反向代理,到最后的升级尝试和回滚。它不是一篇“照着复制就一定成功”的教程,更像是一次把过程摊开来的折腾日志。
起点
我想给 gloriar.com 搭一个属于自己的博客。
最后选下来的组合是:
- 后端:Mix Space /
mx-server - 前端:Yohaku
- 数据库:PostgreSQL
- 缓存:Redis
- 入口:1Panel 里的 OpenResty 反向代理
- 部署方式:Docker Compose
现在稳定运行的结构大概是这样:
访客
│
▼
https://gloriar.com
│
├─ / -> Yohaku 前端 :2323
├─ /about -> Yohaku 前端 :2323
├─ /message -> Yohaku 前端 :2323
│
├─ /api/v2/* -> Mix Space 后端 :2333
├─ /proxy/qaqdmin -> Mix Space 后台 :2333
├─ /feed -> Mix Space 后端 :2333
└─ /sitemap -> Mix Space 后端 :2333
这套结构的好处是:访客看到的是 Yohaku 的页面,后台和 API 仍然由 Mix Space 接管;对外只暴露一个域名,看起来比较干净。
后端:先把 Mix Space 跑起来
后端放在:
/root/mx-space/core
核心服务包括:
mx-server # Mix Space 后端
mx-postgres # PostgreSQL
mx-redis # Redis
mx-migrate # 启动前执行迁移的一次性容器
一开始最容易踩坑的是版本变化。新版 Mix Space 已经不再是早期那种只依赖 MongoDB 的形态,而是需要 PostgreSQL。
所以 compose 里最关键的是这几组环境变量:
PG_HOST: mx-postgres
PG_PORT: "5432"
PG_USER: ${PG_USER:-mx}
PG_PASSWORD: ${PG_PASSWORD:-mx}
PG_DATABASE: ${PG_DATABASE:-mx_core}
REDIS_HOST: mx-redis
REDIS_PORT: "6379"
ALLOWED_ORIGINS: https://gloriar.com,http://localhost:2323,http://127.0.0.1:2323
BETTER_AUTH_URL: https://gloriar.com
其中 BETTER_AUTH_URL 这个变量很容易漏。只写进 .env 还不够,还要确认 docker-compose.yml 真的把它传进容器。否则后台登录、Passkey / WebAuthn 一类功能会出现一些看起来很“玄学”的问题。
后端起来之后,最小验证是:
curl http://127.0.0.1:2333/api/v2/ping
期望返回:
{"data":"pong"}
这个接口很小,但很重要。它能确认后端应用、数据库连接、容器端口至少是通的。
前端:Yohaku 作为访客侧主题
Yohaku 放在:
/root/yohaku-docker
当前稳定镜像是:
ghcr.io/db52/yohaku:sha-0c803b3
Compose 里主要是这些配置:
services:
yohaku:
image: ghcr.io/db52/yohaku:sha-0c803b3
pull_policy: if_not_present
environment:
- NEXT_PUBLIC_API_URL=https://gloriar.com/api/v2
- NEXT_PUBLIC_GATEWAY_URL=https://gloriar.com
- BASE_URL=https://gloriar.com
ports:
- 127.0.0.1:2323:2323
这里我选择只把 2323 绑定在 127.0.0.1,不直接对公网开放。外部访问全部交给 OpenResty。
这样一来,前端容器只需要专心服务页面,HTTPS、域名、路径分流都在反代层处理。
反向代理:同一个域名下分流
OpenResty 是 1Panel 管的容器,不是宿主机裸装的 nginx。
这点非常关键。
配置文件实际在:
/opt/1panel/1panel/www/sites/gloriar.com/proxy/root.conf
改完之后需要在 OpenResty 容器里 reload:
docker exec 1Panel-openresty-rQKp nginx -t
docker exec 1Panel-openresty-rQKp nginx -s reload
主要分流规则是:
location ^~ /api/v2 {
proxy_pass http://127.0.0.1:2333;
}
location ^~ /proxy {
proxy_pass http://127.0.0.1:2333;
}
location ^~ /render {
proxy_pass http://127.0.0.1:2333;
}
location ^~ /socket.io {
proxy_pass http://127.0.0.1:2333;
}
location ^~ / {
proxy_pass http://127.0.0.1:2323;
}
后来又补了 /feed、/atom.xml、/sitemap,否则 RSS / Sitemap 会落到 Yohaku 前端那边,出现 500 或找不到路由。
也就是说,这个站点不是简单地“一个容器一个域名”,而是在同一个域名下把前端和后端路径拆开。
初始化内容:让首页先活起来
部署过程里还有一个小坑:站点刚初始化时内容太少,某些聚合接口和首页逻辑会踩到边界情况。
比如没有 note 的时候,首页或 /notes 可能会出现:
error.api_fetchError
Cannot read properties of null
最后通过补一条最小的 note,让首页依赖的数据结构完整起来。
这件事挺像搭房子时先放一张桌子:它不是最终内容,却能让空间开始成立。
页面与导航:不是所有入口都该靠前端注入
后来我加了几个页面,比如:
/about/bangumi/message
一开始很容易想到用前端注入链接,但这不是最稳的方式。
Yohaku 顶部 Home 下拉里的页面,实际上来自 Mix Space 的 pages 数据。也就是说,如果想让 /message 和 /about 同级,正确做法是创建一个真实页面记录:
title: 留言
slug: message
order: 3
这样 Yohaku 会自然把它列出来,而不是靠一段临时 JS 去“假装存在”。
留言页后来也补了一点内容,不再只是单调的一句话:
风偶尔会停下来,文字也是。
如果你路过这里,想打个招呼、聊聊最近看到的东西,或者只是留下一句无关紧要的话,都可以写在下面。
这种内容不复杂,但它让页面有了自己的气质。
更新与回滚:不要迷信 latest
这次部署里最大的教训是:不要迷信 latest。
Mix Space 和 Yohaku 都更新很快,而且两边是强耦合的。后端 API 形态变了,前端不一定马上兼容;前端新镜像改了读取假设,旧数据也可能让它炸。
我后来试过 v13。
官方 v12 → v13 文档里说得很清楚:
/api/v2/*改成/api/v3/*- 响应结构变成
{ data, meta } - 字段统一
snake_case - 数据库本身不需要迁移
- 前端消费者需要适配
@mx-space/api-client@5或 V3 envelope
后端 v13 本身可以启动,/api/v3/aggregate 也能返回 200。
但最新 Yohaku 配合 v3 后端时,首页和 OG 仍然会炸:
/ -> 500
/home-og -> 500
日志里是:
Cannot destructure property 'seo' of '(intermediate value)' as it is null
Cannot read properties of null (reading '$serialized')
所以最后还是回退到稳定组合:
mx-server: local/mx-server-rollback:20260524-145133
Yohaku: ghcr.io/db52/yohaku:sha-0c803b3
API: /api/v2
回滚不是失败。很多时候,回滚是让服务继续稳定在线的必要动作。
我现在的检查清单
每次更新之后,我都会至少检查这些:
curl -I https://gloriar.com/
curl -I https://gloriar.com/about
curl -I https://gloriar.com/message
curl -I https://gloriar.com/home-og
curl https://gloriar.com/api/v2/ping
对应期望:
| 路径 | 期望 |
|---|---|
/ | 200 |
/about | 200 |
/message | 200 |
/home-og | 200 image/png |
/api/v2/ping | {"data":"pong"} |
只看容器 Up 是不够的。容器活着,不代表首页能 SSR;API 200,也不代表前端能正确吃下数据。
最后
这次部署不像“按教程一步一步就完成”的那种过程,更像是很多小判断叠在一起:
- 哪些路径该走后端,哪些路径该走前端;
- 哪些配置写了但没有传进容器;
- 哪些错误是数据边界,哪些错误是版本不兼容;
- 什么时候该继续修,什么时候该先回退。
现在回头看,这个博客真正被搭起来,不只是因为几个容器跑起来了。
而是因为它终于有了一个比较清晰的边界:
内容在 Mix Space,呈现在 Yohaku,入口交给 OpenResty。
剩下的,就是慢慢往里面写东西了。