Cal.com 是开源的在线预约和日程管理系统,适合咨询预约、面试排期、客户演示、会议室预订和团队轮值。把 Cal.com 放到自己的 VPS 上,可以掌握预约数据、品牌域名和邮件配置,也能避免按席位持续支付 SaaS 费用。代价是你需要自己维护数据库、SMTP、OAuth、备份和升级。
本文以 Ubuntu 24.04、Docker Compose、PostgreSQL 和 Caddy 为例,部署一个适合小团队使用的 Cal.com 单机环境。Cal.com 不同版本的环境变量和数据库迁移可能变化,生产部署前请以对应版本的官方文档和 Compose 模板为准。
Cal.com 本身不算重,但 Next.js 应用、PostgreSQL、构建缓存和邮件任务会共同占用资源。建议配置如下:
| 项目 | 测试环境 | 小团队生产环境 |
|---|---|---|
| CPU | 2 vCPU | 4 vCPU 起 |
| 内存 | 2 GB | 4 GB 起 |
| 磁盘 | 30 GB SSD | 60 GB SSD 起 |
| 系统 | Ubuntu 22.04/24.04 | Ubuntu 24.04 LTS |
| 域名 | cal.example.com | 独立预约子域名 |
为域名添加 A/AAAA 记录指向 VPS。云防火墙只开放 SSH、80 和 443,PostgreSQL 不要映射到公网。如果一台 VPS 已经运行多个 Docker 应用,先用 free -h、df -h 和 docker stats 检查余量;VPS 资源规划可以参考 VPS 配置怎么选。
sudo apt update
sudo apt install -y ca-certificates curl openssl
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker "$USER"
newgrp docker
sudo mkdir -p /opt/calcom
sudo chown -R "$USER":"$USER" /opt/calcom
cd /opt/calcom
为数据库和应用生成随机密码。不要把真实密钥提交到 Git:
openssl rand -hex 32
openssl rand -base64 32
Cal.com 官方发布会提供对应版本的容器和环境变量模板。下面是部署结构示例,镜像标签、启动命令和变量名称请按当前版本调整:
services:
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: calcom
POSTGRES_USER: calcom
POSTGRES_PASSWORD: change-a-long-password
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U calcom -d calcom"]
interval: 10s
timeout: 5s
retries: 10
calcom:
image: calcom/cal.com:latest
restart: unless-stopped
depends_on:
db:
condition: service_healthy
environment:
DATABASE_URL: postgresql://calcom:change-a-long-password@db:5432/calcom
NEXT_PUBLIC_WEBAPP_URL: https://cal.example.com
NEXTAUTH_URL: https://cal.example.com
NEXTAUTH_SECRET: change-another-long-secret
CALENDSO_ENCRYPTION_KEY: change-a-32-byte-key
NODE_ENV: production
ports:
- "127.0.0.1:3000:3000"
volumes:
postgres_data:
生产环境不要直接依赖 latest。确认当前稳定版本后固定镜像标签,并把密码放入 .env 或 Docker secrets。修改 Compose 后先检查展开结果:
docker compose config
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=150 calcom
如果官方镜像需要单独执行数据库迁移或使用不同的启动入口,按该版本文档执行,不要用一个版本的命令套到另一个版本。
确认容器健康后,通过 SSH 隧道临时访问:
ssh -L 3000:127.0.0.1:3000 root@SERVER_IP
浏览器打开 http://127.0.0.1:3000,完成首次管理员注册。正式域名接入前检查:
NEXT_PUBLIC_WEBAPP_URL与实际 HTTPS 域名完全一致;NEXTAUTH_URL没有多余的路径或尾部斜杠;- 加密密钥在重启后保持不变;
- 数据库连接使用 Compose 服务名
db,不是localhost; - 注册和登录邮件的发件域名已准备好。
在宿主机安装 Caddy:
sudo apt install -y caddy
sudo nano /etc/caddy/Caddyfile
写入最小反向代理配置:
cal.example.com {
encode gzip zstd
reverse_proxy 127.0.0.1:3000
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
}
}
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
curl -I https://cal.example.com
如果 VPS 上已经使用 Nginx Proxy Manager 或另一套 Caddy,只保留一个服务监听 80/443。反向代理和 WebSocket 排错方法可参考 VPS 用 Caddy 反向代理完全指南。
没有可靠的 SMTP,预约确认、取消通知和密码重置都会失败。为 Cal.com 配置事务邮件服务时,至少准备:
- SMTP 主机、端口、用户名和应用专用密码;
- 发件人地址和显示名称;
- 域名的 SPF、DKIM、DMARC;
- 一封测试预约邮件和一封取消邮件。
Google Calendar、Microsoft 365 等外部日历需要 OAuth 应用。回调地址必须使用最终 HTTPS 域名,例如:
https://cal.example.com/api/auth/callback/google
OAuth 客户端的回调地址、允许来源和 Cal.com 的环境变量必须逐字一致。不要在前端代码或公开仓库中写入 Client Secret;不同团队成员使用独立连接,撤销权限时只影响对应账号。
首次管理员登录后,建议按这个顺序配置:
- 设置站点名称、时区和默认工作周;
- 创建团队并邀请成员;
- 为 30 分钟咨询、60 分钟演示等场景建立 Event Type;
- 为每个成员连接日历并设置忙闲规则;
- 配置缓冲时间、最短提前预约时间和取消截止时间;
- 设置公开预约链接,私密事件只通过邀请分享;
- 用访客账号测试预约、改期、取消和时区显示。
预约系统的常见问题不是页面打不开,而是时区、忙闲状态或邮件链接不一致。上线前至少用桌面和手机各测试一次,并用不同地区的时区检查可预约时间。
Cal.com 的核心数据在 PostgreSQL,此外还要保存 .env、Compose 文件和加密密钥。每天导出数据库:
mkdir -p /opt/calcom/backups
docker compose exec -T db pg_dump -U calcom -d calcom | gzip > /opt/calcom/backups/calcom-$(date +%F).sql.gz
cp .env docker-compose.yml /opt/calcom/backups/
备份不要只留在同一台 VPS,至少同步到异地对象存储并加密。恢复时先在干净 VPS 建立相同版本,再导入数据库并验证登录、事件类型、日历连接和邮件。可参考 VPS 备份恢复演练。
升级前固定当前镜像标签并保存配置:
docker compose config > /opt/calcom/backups/compose-$(date +%F).yaml
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=200 calcom
先在测试实例执行数据库迁移,再安排生产升级。不要在没有数据库备份和回滚方案的情况下直接跟随 latest。
docker compose ps
docker compose logs --tail=200 calcom
sudo journalctl -u caddy -n 100 --no-pager
ss -lntp | grep -E ':3000|:80|:443'
确认应用绑定的是 127.0.0.1:3000,Caddy 指向相同端口,且容器没有因内存不足退出。
检查 OAuth 控制台中的回调地址、Cal.com 的公开 URL、浏览器当前域名和系统时间。HTTP/HTTPS 混用、域名多一个斜杠或通过临时 IP 访问,都会导致回调拒绝。
先看 Cal.com 容器日志,再检查 SMTP 端口、发件域名 DNS、供应商退信记录和垃圾邮件文件夹。不要为了测试把 SMTP 密码写进 Compose 并提交到公开仓库。
- 域名、HTTPS、登录和预约流程正常;
- SMTP、SPF、DKIM、DMARC 已验证;
- OAuth 回调地址与最终域名一致;
- PostgreSQL 未暴露公网;
.env、数据库和加密密钥都有异地备份;- 完成过一次恢复演练;
- 已固定版本并记录升级回滚步骤。
Cal.com 适合把预约流程掌握在自己的 VPS 上,但它的稳定性取决于域名、日历、邮件和数据库是否一起配置正确。先在测试项目验证完整预约链路,再开放公开链接,后续维护会简单很多。
