Cal.diy 日常运维与排障手册
Cal.diy v6.2.0 日常运维与排障:混合大小写表结构规则、pg_dump 备份与恢复演练(522K/131 段实测)、版本升级七步流程与回退预案、Webhook 注册与 BOOKING_CREATED 推送 HMAC-SHA256 验签实测、启动/访问/SQL/升级四类故障排查。
Cal.diy 日常运维与排障手册
目标:为 Cal.diy v6.2.0 自托管环境提供日常运维手册:数据库维护与备份恢复、版本升级流程、Webhook 管理与出站集成验证、常见故障排查。全部命令与回显均在 Docker Compose 实测环境(Web 8127 / API v2 8129 / 数据库维护口 8128)取得,实测日期 2026-10-19。
一、环境信息
- 部署形态:《Cal.diy Docker Compose 部署指南》产物:
cal-com-6-2-0-web(calcom/cal.com:v6.2.0)、cal-com-6-2-0-db(postgres:16)、cal-com-6-2-0-redis(redis:7)、cal-com-6-2-0-api(自构建 v6.2.0) - 软件版本:Cal.diy v6.2.0(2026-03-01 稳定版);数据层 PostgreSQL 16,Prisma 迁移 588 个(2026-10-19 实查)
- 运维入口约定:数据库操作统一
docker exec cal-com-6-2-0-db psql -U cal_com -d cal_com
二、数据库维护
2.1 表结构命名规则(先读,避免撞错)
Cal.diy 的 Prisma schema 对表名采用混合大小写(PascalCase),列名采用驼峰,少数表映射为小写。实测两条例子:
# 错误:SELECT * FROM passwords; → ERROR: relation "passwords" does not exist
# 正确:
# docker exec cal-com-6-2-0-db psql -U cal_com -d cal_com -c '\dt' | head -8
List of relations
Schema | Name | Type | Owner
--------+--------------------------+-------+---------
public | Account | table | cal_com
public | ApiKey | table | cal_com
public | App | table | cal_com
public | Booking | table | cal_com
public | users | table | cal_com
要点:users、Membership 等部分表小写,"Booking"、"Schedule"、"EventType"、"Deployment" 等核心业务表 PascalCase;混合大小写标识符在任何 SQL 中必须用双引号包裹("Booking"."startTime")。写维护脚本前先 \dt 确认实际表名。
2.2 例行备份与恢复演练
逻辑备份(实测 exit=0,当前库 522K):
# docker exec cal-com-6-2-0-db pg_dump -U cal_com -d cal_com > /tmp/cal_com_backup.sql
# echo $?
0
# ls -lh /tmp/cal_com_backup.sql | awk '{print $5}'
522K
建议频率:每日一次全量(cron 调度可参考站内《Cronicle》或《Dagu》),保留策略按团队规范;备份文件务必异机存放。
恢复演练(实测到临时库验证,不污染主库):
# docker exec cal-com-6-2-0-db psql -U cal_com -c "CREATE DATABASE cal_com_restore_test;"
CREATE DATABASE
# docker exec -i cal-com-6-2-0-db psql -U cal_com -d cal_com_restore_test < /tmp/cal_com_backup.sql
(大量 ALTER TABLE / COPY 输出,结束无 ERROR 即成功)
# docker exec cal-com-6-2-0-db psql -U cal_com -d cal_com_restore_test \
-c 'SELECT count(*) FROM users;' -c 'SELECT count(*) FROM "Booking";'
count
-------
1
count
-------
2
# docker exec cal-com-6-2-0-db psql -U cal_com -c "DROP DATABASE cal_com_restore_test;"
DROP DATABASE
真实故障恢复即把上述目标库换为 cal_com(先停止 Web 与 API 服务避免写入竞争)。
2.3 常用维护 SQL
-- 用户清单与角色
SELECT id, email, username, role FROM users;
-- 管理员提权(首个用户)
UPDATE users SET role='ADMIN' WHERE id=1;
-- 补全显示名与时区(预约标题里的 Nameless 就来自 name 为空)
UPDATE users SET name='管理员', "timeZone"='Asia/Shanghai' WHERE id=1;
-- 预约记录核对
SELECT uid, title, status, "startTime" FROM "Booking" ORDER BY id DESC LIMIT 5;
-- API Key 清单(哈希存储,无法查看明文)
SELECT id, note, "expiresAt", "createdAt" FROM "ApiKey";
-- 部署/license 表(API v2 license 校验相关,社区版通常为空)
SELECT * FROM "Deployment";
2.4 数据库结构迁移
应用每次启动自动执行 npx prisma migrate deploy,日志以 All migrations have been successfully applied. 收尾。手工核对迁移状态:
# docker exec cal-com-6-2-0-db psql -U cal_com -d cal_com \
-c "SELECT count(*) AS applied FROM _prisma_migrations;"
applied
---------
588
(v6.2.0 空库全新部署实测值 588。)升级版本后该计数增长即迁移成功;若出现 failed 状态行,先处理失败原因再重启应用容器重试,不要手工删 _prisma_migrations 行。
三、版本升级流程
官方升级文档(https://cal.diy/upgrading,2026-10-19 实查)的流程与 compose 部署对应如下:
- 备份:按 2.2 完成数据库全量备份并验证可恢复。
- 确认目标版本:订阅
https://github.com/calcom/cal.diy/releases.atom;使用普通 release(非 Pre-release)tag。版本节奏参考:v6.1.10(2026-02-02)→ v6.2.0(2026-03-01)一个月 8 个版本,更新频繁,建议按月跟进。 - 修改镜像 tag:编辑 docker-compose.yml 中
calcom/cal.com:v6.2.0为目标版本(如v6.3.0)。若自构建 API v2 镜像,同步用新版本源码 tag 重建cal-com-6-2-0-api。 - 滚动更新:
# docker compose pull calcom
# docker compose up -d
- 核对迁移:
docker logs cal-com-6-2-0-web 2>&1 | grep -c "Applying migration"观察新迁移执行;2.4 的_prisma_migrations计数应增长到新版本数量。 - 功能抽查:登录、打开预约页、API v2
/v2/me三项冒烟。 - 回退预案:compose 改回旧 tag 重新
up -d即回滚应用;数据库结构升级一般不可自动回退,此时用第 1 步备份按 2.2 恢复——这是每次升级前必须备份的原因。
注意:PostgreSQL 大版本升级(如 15→16)数据卷不兼容,需要 pg_dump 逻辑迁移而非直接复用卷;本文实测基线即 postgres:16,新部署直接使用 16 及以上。
四、Webhook 与工作流管理
4.1 Cal.diy 的工作流能力边界(先说结论)
Cal.diy 社区版不含 Workflows 模块(多步自动化提醒序列随 EE 代码移除,《Cal.diy 介绍与选型指南》第三章有说明)。出站集成的标准形态是 Webhook:预约生命周期事件推送到外部系统,由外部系统完成 IM 通知、工单、订阅邮件等自动化。官方内置的邮件提醒保留基础形态(依赖 SMTP 配置)。
4.2 注册 Webhook(实测)
界面路径:「设置 - Webhook - 新建」。curl 复现实测(tRPC 通道):
# curl -s -b /tmp/cal_cookies.txt -X POST \
'http://192.168.146.128:8127/api/trpc/webhook/create?batch=1' \
-H 'Content-Type: application/json' \
-d '{"0":{"json":{"subscriberUrl":"http://192.168.146.128:8130/hook",
"eventTriggers":["BOOKING_CREATED","BOOKING_CANCELLED"],
"active":true,"payloadTemplate":null,"secret":"whsec_doc_test_2026"}}}'
[{"result":{"data":{"json":{"id":"f479ebca-ce24-4b24-8585-54b4b04e300a","userId":1,
"subscriberUrl":"http://192.168.146.128:8130/hook","active":true,
"eventTriggers":["BOOKING_CREATED","BOOKING_CANCELLED"],
"secret":"whsec_doc_test_2026",...}}}}]
可订阅的触发事件枚举(源码 packages/features/webhooks/lib/constants.ts 实查):BOOKING_CANCELLED、BOOKING_CREATED、BOOKING_RESCHEDULED、BOOKING_PAID、BOOKING_PAYMENT_INITIATED、MEETING_ENDED、MEETING_STARTED、BOOKING_REQUESTED、BOOKING_REJECTED、RECORDING_READY 等。eventTypeId 省略表示作用于全部事件类型。
4.3 推送验证与签名验真(实测)
在接收端收到 BOOKING_CREATED 推送的实际报文(预约者「李四」下单触发):
Content-Type: application/json
X-Cal-Signature-256: 3a8ebe02c7e71580e524b352109ae48da948cf49bce37f26c2c5e0a28f9e7ecf
{
"triggerEvent": "BOOKING_CREATED",
"createdAt": "2026-10-19T12:14:09.546Z",
"payload": {
"uid": "pjJ9sfhN6GRBVDykUhZioW",
"title": "技术交流 between 管理员 and 李四",
"startTime": "...", "endTime": "...",
"type": "技术交流", "eventTypeId": 1, ...
}
}
签名算法为 HMAC-SHA256(secret, 完整请求体),接收端用注册时的 secret 逐字节验证(实测 Python 复核 MATCH):
import hmac, hashlib
computed = hmac.new(b'whsec_doc_test_2026', body_bytes, hashlib.sha256).hexdigest()
assert hmac.compare_digest(computed, received_sig)
验签不通过直接丢弃请求,防止伪造预约事件。运维要点:接收端 URL 必须从服务器可达(compose 网络内服务用宿主 IP 访问外部地址没问题,反之接收端在内网时要用宿主可达地址);推送失败不会重试阻塞业务,但会写应用日志。
4.4 Webhook 日常管理
-- 查看已注册 webhook
SELECT id, "subscriberUrl", active, "eventTriggers" FROM "Webhook";
-- 临时停用(排查接收端故障时)
UPDATE "Webhook" SET active=false WHERE id='f479ebca-...';
删除在界面「设置 - Webhook」操作或 DELETE FROM "Webhook" WHERE ...。
五、日常巡检清单
# 1. 服务健康
# docker compose ps
NAME IMAGE STATUS
cal-com-6-2-0-web calcom/cal.com:v6.2.0 Up X days (healthy)
cal-com-6-2-0-db postgres:16 Up X days (healthy)
cal-com-6-2-0-redis redis:7 Up X days
# 2. 应用入口
# curl -s -o /dev/null -w "%{http_code}\n" http://192.168.146.128:8127/
200
# curl -s -o /dev/null -w "%{http_code}\n" http://192.168.146.128:8129/v2/me
401 ← API v2 就绪时返回业务级 401(未带凭据),502/404 才是异常
# 3. 错误日志扫描(近 200 行内无 fatal/unhandled 即可)
# docker logs --tail 200 cal-com-6-2-0-web 2>&1 | grep -ci "fatal\|unhandled"
0
# 4. 内存水位(四个服务 mem_limit 3.6G)
# docker stats --no-stream --format '{{.Name}}\t{{.MemUsage}}' | grep cal-com
# 5. 备份文件按日更新
# ls -lht /backup/cal_com_*.sql | head -3
六、常见故障排查
6.1 启动类
Web 容器反复重启或 502:
# docker logs cal-com-6-2-0-web 2>&1 | tail -20
常见三类:数据库连接串错(DATABASE_URL 密码特殊字符未转义)、迁移中断(重启容器让 migrate deploy 幂等重试)、内存不足被 OOM(docker inspect cal-com-6-2-0-web --format '{{.State.OOMKilled}}',调大 compose mem_limit)。
启动日志刷 Unknown argument: database(wait-for-it.sh 缺陷):v6.2.0 上游脚本以裸主机名调用 wait-for-it.sh 导致的报错(要求 host:port 格式),实测脚本报错后继续执行迁移,服务正常拉起,可忽略;若偶发迁移期数据库未就绪,docker compose restart calcom 重试。
API v2 崩溃循环 Missing environment variable: STRIPE_API_KEY.:环境变量必须存在(自托管留空即可)。.env 补 STRIPE_API_KEY= 与 STRIPE_WEBHOOK_SECRET= 后 docker compose up -d calcom-api。
API v2 报 502/404:502 为服务未就绪(看日志最后三行应出现 RedisService IoRedis connected!);404 检查路径前缀(服务挂在 /v2/)与端口映射。
6.2 访问与认证类
页面 403:ALLOWED_HOSTNAMES 未包含访问用的主机名:端口,修改后 --force-recreate 重建 Web 容器。
登录循环回登录页:NEXTAUTH_URL 与实际访问地址不一致导致 Cookie 域不匹配;三处 URL 变量(NEXTAUTH_URL、NEXT_PUBLIC_WEBAPP_URL、NEXT_PUBLIC_WEBSITE_URL)必须与浏览器地址栏完全一致。
管理后台看不到:首个用户需按《Cal.diy 快速入门与实战示例》2.2 提权;INACTIVE_ADMIN 是激活状态提示而非权限缺失,管理页面实际可达。
API v2 全部 401 Invalid or missing CALCOM_LICENSE_KEY:社区版 API Key 通道的企业 license 门槛(API Key 校验已通过,卡在 license 检查)。用 Web 会话 Cookie 通道(NEXT_AUTH)替代,或采购 license。此为实测确认的社区版行为,非配置故障。
6.3 SQL/数据类
relation "xxx" does not exist:表名大小写问题,先 \dt 再带双引号(2.1)。
column "start_time" does not exist:列名是驼峰("startTime"),不是下划线。
预约数据丢失嫌疑:先核对库再怀疑应用——SELECT count(*) FROM "Booking"; 与最近备份比对;确认连的是同一个卷(docker volume inspect cal-diy_database-data)。
API 建资源后页面看不到:API v2 与 Web 共库,数据实时互通;多为账号/团队过滤器不同(API 用的 userId 与页面登录账号不一致)。
6.4 升级类
升级后启动失败:先看是不是目标版本要求新的环境变量(v6.2.0 曾要求 STRIPE_API_KEY 占位);迁移失败按 2.4 处理;回退按第三章第 7 步。
升级后预约页样式错乱:浏览器缓存旧静态资源,强刷(Ctrl+Shift+R)或等 CDN/浏览器缓存过期;NEXT_PUBLIC_* 变量变更必须 --force-recreate 重建。
七、参考链接
- 官方升级文档:
https://cal.diy/upgrading - 官方排障文档:
https://cal.diy/troubleshooting - 官方数据库迁移说明:
https://cal.diy/database-migrations - Release 流:
https://github.com/calcom/cal.diy/releases.atom - 站内同批文档:《Cal.diy 介绍与选型指南》《Cal.diy Docker Compose 部署指南》《Cal.diy 快速入门与实战示例》