引言
市面上待办 App 很多——Todoist、Things 3、Microsoft To Do、TickTick……但都面临同一个问题:数据在别人手里。你的任务列表、到期日、优先级,全存在 SaaS 厂商的服务器上。哪天服务涨价、关停、或者你只是想换个客户端,数据迁移就是一场噩梦。
自部署待办系统的好处显而易见:
- 数据完全自主——存在自己的服务器上,不经过第三方
- 隐私——任务内容不上传云端
- 零订阅费——只需一台小服务器(树莓派、NAS、云服务器都行)
- 自由选择客户端——直接搭配手机自带日历/待办 App,不需要装额外应用
本文介绍 Vikunja——一个开源、现代、功能完善的自部署待办管理平台。用 Docker Compose 一键部署,搭配 Cloudflare Tunnel 安全暴露到公网,再用 CalDAV 协议同步到手机,打造一套”零成本、高隐私”的待办管理系统。
Vikunja 是什么
Vikunja(发音 /viːˈkuːnjə/)是一个用 Go 编写的开源待办任务管理平台,AGPLv3 协议许可。它的定位介于轻量待办(如 Todoist)和重量级项目管理(如 Jira/OpenProject)之间。
核心功能一览:
| 功能 | 说明 |
|---|---|
| 项目/列表 | 按项目组织任务,支持嵌套层级 |
| 看板视图 | 以 Kanban Board 形式查看任务 |
| Gantt 图表 | 可视化任务的时间依赖关系 |
| 标签 & 优先级 | 用标签分类,四级优先级标记 |
| 任务关联 | 关联到另一个任务(依赖/引用关系) |
| 团队协作 | 邀请其他用户协作同一项目 |
| 重复任务 | 支持 RRULE 规则的周期性任务 |
| 提醒通知 | 邮件通知 + Webhook |
| 公开分享 | 生成分享链接给外部用户(无需登录) |
| CalDAV 同步 | 通过开放协议同步到手机/其他应用 |
| REST API | 完整的 API,可编程操作所有功能 |
| 全平台支持 | Web 端 + Linux/Windows/macOS 桌面端 + iOS/Android 客户端 |
和主流待办工具的对比:
| Vikunja | Todoist | Things 3 | Microsoft To Do | |
|---|---|---|---|---|
| 开源 | ✅ AGPLv3 | ❌ | ❌ | ❌ |
| 自部署 | ✅ | ❌ | ❌ | ❌ |
| CalDAV 同步 | ✅(实验性) | ❌ | ❌ | ❌ |
| 看板视图 | ✅ | ✅ | ❌ | ✅ |
| 团队协作 | ✅ | ✅(付费) | ❌ | ✅ |
| 价格 | 免费 | 免费/付费 | ¥328 买断 | 免费 |
自部署意味着你完全掌控数据——但如果不想自己维护服务器,Vikunja 官方也提供托管版 Vikunja Cloud,需要付费订阅。
一、Docker Compose 部署
Vikunja 官方推荐用 Docker Compose 部署。核心架构是两个容器:Vikunja 应用(Go 二进制) + PostgreSQL 数据库。
准备工作
在一台有 Docker 和 Docker Compose 的 Linux 服务器上操作。需要什么配置?门槛很低:
- 内存:最低 512MB,推荐 1GB+
- CPU:单核即可
- 磁盘:初始占用 ~200MB(不含数据库数据)
- 操作系统:支持 Docker 的任何 Linux 发行版
适合部署的位置:闲置的树莓派、NAS(群晖/飞牛/unRAID)、云服务器(阿里云/腾讯云/Vultr 最低配都绰绰有余)。
创建项目目录
mkdir -p /path/to/vikunja
cd /path/to/vikunja
mkdir files # 存储用户上传的附件、项目背景图
mkdir -p db # PostgreSQL 数据目录
chown 1000 files # ⚠️ 重要:Vikunja 容器以 UID 1000 运行
docker-compose.yml
services:
vikunja:
image: vikunja/vikunja:latest
environment:
VIKUNJA_SERVICE_PUBLICURL: https://todo.yourdomain.com
VIKUNJA_DATABASE_HOST: db
VIKUNJA_DATABASE_PASSWORD: changeme
VIKUNJA_DATABASE_TYPE: postgres
VIKUNJA_DATABASE_USER: vikunja
VIKUNJA_DATABASE_DATABASE: vikunja
VIKUNJA_SERVICE_SECRET: <生成一个随机密钥>
VIKUNJA_SERVICE_ENABLEREGISTRATION: true # 首次部署开启注册
ports:
- "3456:3456"
volumes:
- ./files:/app/vikunja/files
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: changeme
POSTGRES_USER: vikunja
volumes:
- ./db:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -h localhost -U vikunja"]
interval: 5s
start_period: 30s
几个关键配置说明:
VIKUNJA_SERVICE_PUBLICURL:填最终用户访问的地址(含协议和路径)。如果用了反向代理/Cloudflare Tunnel,填公网域名;如果只是局域网用,填http://192.168.x.x:3456。注意:如果端口不是 80/443,URL 中必须包含端口号,否则注册账号时会返回「unauthorized」错误。VIKUNJA_SERVICE_SECRET:用于 JWT 签名。用openssl rand -base64 32生成,不要用示例中的值。VIKUNJA_SERVICE_ENABLEREGISTRATION: true:首次部署先开启注册,建好管理员账号后关掉,防止陌生人注册。
启动
docker compose up -d
检查启动日志:
docker compose logs -f
看到类似输出说明启动成功:
vikunja_1 | ⇨ http server started on [::]:3456
vikunja_1 | INFO ▶ Vikunja version 0.24.0
验证 API 是否正常:
curl http://localhost:3456/api/v1/info
返回 JSON 包含版本号即成功。打开浏览器访问 http://服务器IP:3456,应该看到 Vikunja 登录页面。
首次设置
- 注册一个账号(第一个注册的用户默认是管理员)
- 进入「设置 → 用户」关闭公开注册(
Enable Registration: OFF) - 也可以在环境变量中设置
VIKUNJA_SERVICE_ENABLEREGISTRATION: false并重启
注册关闭后如何创建用户
如果关闭了公开注册,再想添加用户只能用 CLI:
docker compose exec vikunja /app/vikunja/vikunja user create \
-u 用户名 \
-e email@example.com \
-p 密码
列出所有用户:
docker compose exec vikunja /app/vikunja/vikunja user list
⚠️ Vikunja 镜像基于
scratch基础镜像——它没有 shell。你无法docker exec -it vikunja sh进入容器。所有 CLI 操作必须通过docker compose exec vikunja /app/vikunja/vikunja <subcommand>执行完整路径。
二、Cloudflare Tunnel 公网暴露
自部署应用的最大痛点:如何安全地从公网访问?
传统方案是在路由器上做端口转发(Port Forwarding),但这意味着:
- 你的公网 IP 直接暴露(容易被扫端口)
- 需要手动管理 SSL 证书
- 家宽 IP 变动后 DDNS 有延迟
- 运营商可能封 80/443 端口
Cloudflare Tunnel(原名 Argo Tunnel)解决了这些问题:你的服务器主动向外连到 Cloudflare Edge,不开放任何入站端口。用户通过 Cloudflare 网络访问,流量全程加密。
整体架构
用户 → https://todo.yourdomain.com
→ Cloudflare Edge (SSL 终结)
→ Cloudflare Tunnel (加密隧道)
→ 本地 cloudflared 容器
→ Vikunja (localhost:3456)
全程没有开放防火墙端口。甚至你的服务器可以在一台没有公网 IP 的设备上(接入 CGNAT 的家宽、5G 路由器都行)。
前提
- 一个在 Cloudflare 管理的域名(免费套餐即可)
- 服务器能出站访问互联网
步骤 1:Cloudflare 侧创建 Tunnel
登录 Cloudflare Dashboard → Zero Trust → Networks → Tunnels → Create a tunnel:
- 选择 Cloudflared 类型
- 给隧道命名(如
vikunja-tunnel) - 复制生成的 Tunnel Token(一串很长的字符串,形如
eyJhIjoi...)
步骤 2:Docker Compose 中增加 cloudflared
在上面的 docker-compose.yml 中增加一个服务:
cloudflared:
image: cloudflare/cloudflared:latest
command: tunnel --no-autoupdate run
environment:
- TUNNEL_TOKEN=你的Tunnel Token
restart: unless-stopped
或者你也可以独立部署 cloudflared(同一个 Docker Compose 文件或单独的项目都行)。
步骤 3:配置公网主机名
Cloudflare Tunnel Dashboard → 你的隧道 → Public Hostnames → Add:
- Subdomain:
todo(或你想要的子域名) - Domain:选择你的域名
- Type:HTTP
- URL:
http://vikunja:3456
这里的 vikunja 是 Docker Compose 中 Vikunja 服务的容器名,Docker 内部 DNS 会自动解析。如果你把 cloudflared 放在独立的 compose 文件中,URL 可能需要填服务器内网 IP(如 http://192.168.1.100:3456)。
保存后,等待几十秒 DNS 生效,访问 https://todo.yourdomain.com 即可看到 Vikunja。
可选:Caddy/Nginx 反代 + Cloudflare Tunnel
如果你的网络架构中有反向代理(如 Caddy、Nginx Proxy Manager、Traefik),Cloudflare Tunnel 也可以指向反代而非直接指向 Vikunja:
用户 → Cloudflare Tunnel → Caddy (端口 443) → Vikunja (端口 3456)
这样你可以在反代层统一管理 SSL、缓存、URL 重写。对 Vikunja 而言,Caddy 配置极其简单:
todo.yourdomain.com {
reverse_proxy 127.0.0.1:3456
}
Docker Hub 镜像拉取限流怎么办
国内网络环境拉 Docker 镜像经常遇到 Hub 限流。备选镜像前缀:
# hub.rat.dev 镜像
docker pull hub.rat.dev/vikunja/vikunja:latest
docker tag hub.rat.dev/vikunja/vikunja:latest vikunja/vikunja:latest
docker pull hub.rat.dev/library/postgres:16
docker tag hub.rat.dev/library/postgres:16 postgres:16
其他可用的镜像加速:docker.1ms.run/、docker.xuanyuan.me/。注意镜像来源的可靠性。
三、CalDAV 手机同步
⚠️ Vikunja 的 CalDAV 集成目前仍处于早期 Alpha 阶段,作者明确标注有 bug。部分客户端工作正常,部分客户端有问题。建议先在非主力设备上测试。
CalDAV 是什么
CalDAV 是一个开放标准协议(RFC 4791),用于在服务器和客户端之间同步日历和待办数据。原生被 iOS/macOS 系统日历和提醒事项支持。Android 端通过第三方 App 支持。Vikunja 实现了 CalDAV VTODO 扩展,可以把任务同步到手机自带的待办 App。
获取 CalDAV 凭证
Vikunja 的 CalDAV 认证有两种方式:
- 账号密码:用你 Vikunja 的用户名 + 登录密码直接认证
- 专用 Token:在 Vikunja「设置 → CalDAV」中生成一个以
tk_开头的专用 token(推荐,更安全)
CalDAV 端点信息
| 项目 | 值 |
|---|---|
| 基准 URL | https://todo.yourdomain.com/dav(或内网 http://192.168.x.x:3456/dav) |
| 认证方式 | Basic Auth(用户名 + 密码/Token) |
| 用户主体 | /principals/<用户名>/ |
| 所有项目 | /projects/ |
| 单个项目 | /projects/<项目ID>/ |
| 单个任务 | /projects/<项目ID>/<任务UID> |
💡 项目 ID 可以在 Vikunja Web 界面打开项目后,从浏览器地址栏的 URL 中看到。
iOS 配置(iPhone/iPad)
iOS 系统自带的「提醒事项」App 支持 CalDAV 同步,无需安装第三方应用:
- 设置 → 日历 → 添加账户 → 其他 → 添加 CalDAV 账户
- 填写:
- 服务器:
todo.yourdomain.com(或内网 IP) - 路径:
/dav - 用户名:你的 Vikunja 用户名
- 密码:CalDAV Token 或登录密码
- 打开「提醒事项」App,会自动加载 Vikunja 的项目作为清单
⚠️ iOS 的提醒事项同步可能会出现:
- 项目列表可能需要手动刷新才能看到
- 首次同步可能耗时较长(取决于任务数量)
- 部分属性(如富文本描述)可能不同步
Android 配置
Android 采用的是双层面架构,需要两个 App 配合:
第一层:同步引擎(DAVx⁵)
DAVx⁵ 负责与 CalDAV 服务器通信,将任务数据拉到手机本地存储。
- 下载:推荐从 F-Droid 下载开源免费版(Google Play 版收费)
- 配置:
- 打开 DAVx⁵ → 添加账户
- 选择 CalDAV 类型
- URL:
https://todo.yourdomain.com/dav或内网地址 - 用户名 + 密码/Token
- 选择需要同步的项目列表
第二层:待办界面(Tasks.org)
Tasks.org 是一个开源、功能丰富的待办 App,通过系统同步框架读取 DAVx⁵ 的数据。
- 下载:F-Droid 或 Google Play(开源免费)
- 配置:
- 如果 DAVx⁵ 和 Tasks.org 都正确安装,Tasks.org 会自动识别 DAVx⁵ 同步过来的清单
- 进入 Tasks.org 设置 → 同步 → 确认 DAVx⁵ 集成已开启
- 同步后的任务会显示在 app 中,可正常编辑、勾选完成
DAVx⁵ 与 Tasks.org 直接 CalDAV 的取舍
Tasks.org 支持两种同步方式:
| 方式 | 优点 | 缺点 |
|---|---|---|
| DAVx⁵ → Tasks.org | 任务数据存本地,离线可用 | 需要在 Tasks.org 中额外配置同步集成 |
| Tasks.org 直接 CalDAV | 一步到位 | 不支持在 Tasks.org 中创建/重命名/删除清单,同步稳定性略差 |
建议首选 DAVx⁵ 方案,离线可用性更好。
受支持的 VTODO 属性
Vikunja 的 CalDAV 支持以下任务属性同步:
已支持:UID、SUMMARY(标题)、DESCRIPTION(描述)、PRIORITY(优先级)、CATEGORIES(标签)、COMPLETED(完成时间)、DUE(截止日期)、DURATION(持续时间)、DTSTART(开始时间)、RRULE(重复规则)、STATUS(状态)、VALARM(提醒)
不支持:ATTACH(附件)、GEO(地理位置)、LOCATION(位置)、URL
四、运维与常见陷阱
部署完成只是第一步。以下是实测中遇到的坑和解决方案:
1. files 目录权限问题
Vikunja 容器内进程以 UID 1000 运行。files/ 目录必须由这个用户拥有,否则上传附件、背景图会报权限拒绝:
open /app/vikunja/files/1: permission denied
解决:
chown 1000:1000 files
# 不要递归 chown,只改顶层目录即可
2. 容器无 Shell,调试靠日志
Vikunja 镜像基于 scratch,是最精简的 Docker 镜像。这意味着:
# ❌ 这些都不行
docker exec -it vikunja sh
docker exec -it vikunja bash
# ✅ 只能这样
docker compose logs -f
docker compose logs vikunja | tail -50
3. VIKUNJA_SERVICE_PUBLICURL 必须正确
这个环境变量决定了 Vikunja 生成的链接、API 请求路径是否正确。如果填错,注册页面会报「unauthorized」错误。部署在反代后时尤其容易出问题:
- 用了 Cloudflare Tunnel → 填
https://todo.yourdomain.com - 局域网直连(无反代)→ 填
http://192.168.x.x:3456(含端口) - 用了反代且反代非标准端口 → 填完整的公网地址
4. Cloudflare Tunnel 配置后 Vikunja 提示数据库连接失败
如果通过 Cloudflare Tunnel 访问 Vikunja 时页面一直加载然后报错,通常是 VIKUNJA_SERVICE_PUBLICURL 配置不正确。Cloudflare Tunnel 改写了一些请求头,Vikunja 需要正确的 PUBLICURL 才能生成有效的内部链接。
解决:确保 VIKUNJA_SERVICE_PUBLICURL 填的是公网 https:// URL,而不是内网 HTTP 地址。
5. Docker Hub 拉取限流
国内频繁拉取 Docker Hub 镜像会遇到 rate limit。除了上面提到的镜像前缀,还可以考虑:
- 用
registry.docker-cn.com等国内镜像站(部分已关闭,需自行测试可用性) - 在 NAS 或其他服务器上部署 Docker Registry Mirror
- 只更新稳定版标签(
vikunja/vikunja:latest更新不频繁,基本不会触限)
6. 数据备份
最小化备份方案:
# 备份数据库 + 上传文件
docker compose exec db pg_dump -U vikunja vikunja > vikunja-db-backup.sql
tar czf vikunja-backup-$(date +%Y%m%d).tar.gz \
vikunja-db-backup.sql \
files/
恢复时重建容器,然后:
cat vikunja-db-backup.sql | docker compose exec -T db psql -U vikunja
7. 关闭注册后的用户管理
如果关闭了公开注册(推荐生产环境这么做),需要添加用户时只能用容器 CLI。由于容器没有 shell,命令格式是:
# 创建用户
docker compose exec vikunja /app/vikunja/vikunja user create \
-u newuser -e newuser@example.com -p securepassword
# 列出所有用户
docker compose exec vikunja /app/vikunja/vikunja user list
8. Docker 容器版本更新
cd /path/to/vikunja
docker compose pull
docker compose up -d
Vikunja 使用数据库迁移自动升级 schema,启动时自动完成。如果升级后出现问题,检查日志:
docker compose logs vikunja | grep -i error
9. 容器数据导出
Vikunja 的数据导出功能需要容器内的 /tmp 目录可写。如果你的 Docker 配置限制了 /tmp 权限,可以在 compose 文件中添加:
volumes:
- /tmp:/tmp
可选:用户端的替代选择
Vikunja 不是唯一的选择。以下是同类型自部署待办工具对比:
| 项目 | 语言 | 数据库 | 看板 | CalDAV | 协作 |
|---|---|---|---|---|---|
| Vikunja | Go | PostgreSQL/MySQL/SQLite | ✅ | ✅(实验性) | ✅ |
| Nextcloud Tasks | PHP | 依赖 Nextcloud | ❌ | ✅ | ✅ |
| OpenProject | Ruby | PostgreSQL | ✅ | ❌ | ✅(重型) |
| Taiga | Python/Django | PostgreSQL | ✅ | ❌ | ✅ |
| Plane | Python/Django | PostgreSQL | ✅ | ❌ | ✅ |
| Huly | TypeScript | MongoDB | ✅ | ❌ | ✅ |
选型建议:
- 如果已经在用 Nextcloud:直接用 Nextcloud Tasks + CalDAV 同步手机,零额外部署
- 如果只想要一个轻量、好看的待办:Vikunja 最合适
- 如果要做项目管理(Gantt 图、工时追踪):OpenProject 或 Plane
- 如果团队用敏捷开发:Taiga 或 Plane
结语
自部署待办管理系统,搭建起来并不比注册一个 Todoist 账号复杂多少。Docker Compose 三分钟部署,Cloudflare Tunnel 五分钟配置,CalDAV 同步手机一步到位。
整个方案的优势:
- 成本:零订阅费(域名年费几十块)
- 隐私:数据全在自己服务器
- 灵活性:Vikunja 有完整 API,可以编程自动化任务管理
- 独立性:不依赖任何 SaaS 服务,服务器在数据就在
当然它也有代价——需要维护一个 Docker 容器、备份数据库、关注版本更新。但对我来说,这种”自己的数据自己管”的掌控感,比省下来的那点订阅费更有价值。
如果你也在找一款自部署的待办工具,不妨试试 Vikunja。有任何问题或者踩了坑,欢迎留言交流。