用Docker自托管Vikunja:私有待办清单部署全攻略
把任务清单这种日常工具自托管起来是很多折腾过Docker的人都会动过的念头。市面上Todo类应用不少但数据在自己手里、界面能用、部署不复杂的开源方案我最后选了Vikunja。它用GoVue写的一个Docker镜像就能跑起来前五分钟我就能在浏览器里看到登录页后面花时间反而不在安装本身而是Docker环境、数据库选型、数据持久化这些细枝末节。这篇文章就按我实际部署的顺序写一遍从Docker环境准备、为什么选Vikunja、单容器快速跑通到compose编排数据库和反向代理、再到安装过程中最常踩的几个报错最后聊几个装完立刻要做的事。如果你正打算在服务器或自己电脑上装一个隐私可控的待办清单工具对Docker只停留在“听说过”的层面这篇就是照着做就能跑通的实操记录。1. 先搞定Docker环境这是拦路的第一道坎1.1 这个报错不等于安装失败先看CPU虚拟化开关最近被问得最多的Docker问题不是Vikunja装不上而是Docker Desktop压根启动不了报错里带着一句virtualization support was not detected。说实话这个问题跟Docker本身关系不大绝大多数情况是Windows的CPU虚拟化没开或者WSL2功能没启用。判断方法很简单打开任务管理器切到“性能”选项卡看左下角的CPU信息里面有个“虚拟化”状态。如果显示“已禁用”直接关机进BIOS不同主板按键不同一般是Del或F2找Intel VT-x或AMD-V/SVM的开关打开后保存重启。这一步做完再启动Docker Desktop大概率就正常了。如果你确认虚拟化已经启用但Docker Desktop还是报同样的错那要注意一下启动方式。Docker Desktop在Windows上有两种后端Hyper-V和WSL2。新版本默认用WSL2这也是我推荐的选择——镜像启动更快、内存占用更可控。但WSL2本身需要在“启用或关闭Windows功能”里把“适用于Linux的Windows子系统”勾上同时“虚拟机平台”也要启用。两个功能都开重启电脑再打开Docker Desktop就会好很多。1.2 Docker Desktop安装完还要调两个设置很多教程到安装完就结束了其实装完Docker Desktop后有两个设置直接决定你后面顺不顺。第一个是设置里的“Resources”内存分配。Docker Desktop默认内存可能是2GB跑Vikunja这种轻量应用够用但如果你同时跑数据库、反代容器建议调到4GB以上。留意别把宿主机内存全部给出去WSL2会动态占用但给得太多会让Windows本身变卡。第二个是镜像加速。国内拉Docker官方镜像经常慢到怀疑人生尤其是第一次拉vikunja/vikunja这种上百MB的镜像。解决办法是找到Docker Desktop的“Docker Engine”配置JSON加入一个国内镜像加速地址不同云厂商提供的加速器地址不一样找一个在你网络环境下速度稳定的就行。配置完重启Docker Desktop再拉镜像速度会上来一大截。“启动docker服务失败”这类报错之前见过不少大多是Docker Desktop安装过程中Windows服务没有注册成功或者跟杀毒软件有冲突。修复路径基本是卸载重装Docker Desktop装完后把Docker相关的几个Windows服务状态确认一遍必要时重启电脑。1.3 Linux服务器上的dockersock权限问题如果你是在云服务器上跑Docker而不是Windows桌面那遇到的第一个报错大概率是这种permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock这个错误特别容易让新手以为是Docker服务没启动其实不是。它只是因为当前用户不在docker用户组里没有权限访问Docker的socket文件。解决方法sudo usermod -aG docker $USER newgrp docker docker run hello-world第一行把自己加进docker组第二行让用户组立刻生效第三行验证。如果还是不行确认一下当前的Docker服务状态sudo systemctl status docker服务状态是active就可以放心往下走了。2. 为什么我选Vikunja以及它和纯Docker单容器方案的匹配点2.1 待办工具那么多选Vikunja的三个理由选Vikunja之前我试过几类方案在线网站的看板工具确实漂亮但任务数据全部放在别人服务器上一旦免费额度收紧或账号出问题数据很难干净导出用文本文件做清单又太原始手机上没办法快速查看和勾选。Vikunja正好在两者之间找到了平衡点开源、可以完全自托管界面却是现代Web应用的水准。它的核心能力覆盖了一个人能用到、小团队也够用的几乎所有场景支持列表视图、看板视图、甘特图视图和表格视图而看板模式下拖拽操作流畅度相当高任务可以设置优先级、截止时间、重复周期和标签还能指派给团队成员每个列表、每个项目都能生成日历订阅链接可以在手机日历里看到截止日期支持PWA方式添加到手机主屏用起来接近原生App最关键的是Vikunja不依赖重型第三方服务一个Docker容器加上可选的数据库就能完整运行。这正是我想要的“能自己掌控一切”的部署方式。2.2 一个容器还是两个容器先读懂Vikunja的架构网上搜Vikunja的安装教程能看到两种完全不同的说法。一种说拉两个镜像一个后端API加一个前端页面部署时要配反代、处理跨域另一种说一个镜像直接跑起来。两种说法都没错但属于不同时期的架构。Vikunja的官方镜像在较新的版本里已经把后端Go写的API服务和前端Vue构建的页面打包在一起了。现在直接从Docker Hub拉vikunja/vikunja这个镜像一个容器就能同时提供前端页面和API接口不再需要单独搞两个容器。这个小细节很重要。很多老教程还在按“两个镜像”的思路写照着做不是不行但多了一堆不必要的配置项对新手很不友好。我下面的操作都是基于新版全栈镜像容器内部前端页面监听80端口API服务在容器内占用3456端口对外我们只需要暴露一个80端口就行。2.3 数据放哪SQLite和PostgreSQL的选择逻辑Vikunja支持三种数据存储方式内置SQLite、PostgreSQL、MySQL/MariaDB。很多第一次部署的人会在这里纠结其实选择逻辑很直接。如果只是个人使用任务量不大、并发很低直接用内置SQLite是最省事的——不用额外管理数据库容器一个镜像把所有事情都干了。但要注意SQLite模式下数据库文件是落在容器里的必须把数据目录持久化出来否则容器一重建任务全丢。如果是多人使用或者部署在公网服务器上建议直接用PostgreSQL。PostgreSQL在并发写入、备份恢复、数据安全性上都明显优于SQLite而且后续做定时备份用pg_dump也顺手。我的建议是不管什么场景既然都用了Docker多跑一个PostgreSQL容器并不复杂踏踏实实把数据放在外置数据库里省得以后再迁移。后面我会给出完整的compose配置。3. 用docker run快速跑起来先让界面出现在浏览器里3.1 最简启动命令与参数逐项拆解先把Vikunja跑起来看看效果是最有成就感的阶段。不急着上compose用一条docker run命令就能完成。单容器快速启动docker run -d \ --name vikunja \ -p 3456:80 \ -v /opt/vikunja/files:/app/vikunja/files \ -v /opt/vikunja/db:/app/vikunja/db \ --restart unless-stopped \ vikunja/vikunja:latest拆开看每个参数的意思-d后台运行不占用当前终端--name vikunja给容器起名字后续docker logs vikunja、docker restart vikunja都靠这个名字-p 3456:80宿主机3456端口映射到容器内80端口浏览器访问http://服务器IP:3456-v /opt/vikunja/files:/app/vikunja/files把容器内附件、上传文件目录挂载到宿主机-v /opt/vikunja/db:/app/vikunja/db把SQLite数据库文件目录挂载出来这个挂载至关重要--restart unless-stopped服务器重启或Docker重启时自动拉起容器这段命令里db目录的挂载很容易被忽略。Vikunja用内置SQLite时数据库文件就存放在这个目录里。只挂载files目录不做db挂载的话容器重建时任务数据会全部归零。3.2 首次登录、初始化管理员和立刻要做的安全设置命令执行完后等几秒钟让容器完成启动然后用浏览器访问http://你的服务器IP:3456首次打开是Vikunja的登录页面。默认管理员账号是admin密码也是admin。第一次进入后立刻去用户设置里把密码改掉同时把个人头像、时区、默认语言设置好。之所以强调这一步是因为如果你把服务映射到了公网默认的admin密码等于把大门敞开了。哪怕是个人NAS或者内网部署养成改默认密码的习惯也很有必要。登录进去之后可以先建一个测试列表随便加两个任务试试。到这里Vikunja已经跑通了你已经拥有了一个完全自托管的待办清单。3.3 单容器方案的两个数据持久化坑docker run跑通之后有两件事不要急着跳过。第一确认挂载目录的属主权限。Vikunja容器内默认以固定的UID运行通常是1000如果宿主机挂载目录的所有者是root或其他用户容器写入数据时可能会报sqlite unable to open database file。保险起见创建目录后执行一下sudo mkdir -p /opt/vikunja/files /opt/vikunja/db sudo chown -R 1000:1000 /opt/vikunja第二改密码、改配置之前想清楚。用docker run方式启动后如果要改配置项通常是用-e环境变量但每次新增环境变量都要重新创建容器。这时候你会发现用compose管理配置要方便得多。所以快速体验结束之后接下来就应该转入compose方案。4. 上生产docker compose 编排数据库和反向代理4.1 compose 文件直接抄全栈镜像 PostgreSQL真正稳定运行我推荐用Docker Compose。下面这个compose文件我直接贴在服务器上就能用services: db: image: postgres:16-alpine restart: unless-stopped environment: POSTGRES_USER: vikunja POSTGRES_PASSWORD: vikunja POSTGRES_DB: vikunja volumes: - db_data:/var/lib/postgresql/data healthcheck: test: [CMD-SHELL, pg_isready -U vikunja] interval: 5s timeout: 3s retries: 5 vikunja: image: vikunja/vikunja:latest restart: unless-stopped depends_on: db: condition: service_healthy ports: - 3456:80 environment: VIKUNJA_DATABASE_TYPE: postgres VIKUNJA_DATABASE_HOST: db VIKUNJA_DATABASE_USER: vikunja VIKUNJA_DATABASE_PASSWORD: vikunja VIKUNJA_DATABASE_NAME: vikunja volumes: - vikunja_files:/app/vikunja/files volumes: db_data: vikunja_files:使用方式mkdir -p /opt/vikunja cd /opt/vikunja # 把上面内容保存成 docker-compose.yml docker compose up -d等待镜像拉取完成后同样访问http://服务器IP:3456看到登录页就说明compose方案也跑通了。4.2 我为什么建议你给Vikunja配独立数据库而不是一直用SQLite有人会问既然SQLite也能跑为什么还要多加一个PostgreSQL容器我个人的体会是这取决于“任务数据”对你的价值。待办清单里的任务可能包含了近期工作安排、家庭事项、项目计划一旦损坏或丢失找回的代价远大于多维护一个容器的成本。PostgreSQL在几个方面比SQLite更适合作为长期数据层备份恢复更成熟pg_dump可以一边运行一边做逻辑备份不用停服务并发写入更稳健多人同时创建任务、勾选任务时不会出现锁等待数据库文件独立存在命名卷里容器升级、镜像重拉本质上不影响数据compose里那个healthcheck也很关键。它保证数据库完全就绪后Vikunja才开始连接。没有这一步经常出现Vikunja先启动、数据库还没打开端口最后报连接失败的情况。4.3 用Caddy把Vikunja放到域名下并自动HTTPS如果只用IP加端口访问到这一步就可以停了。但如果你有自己的域名想让Vikunja跑在标准443端口、自动带HTTPS加一个Caddy容器是最省力的方式。新建一个caddy目录放配置你的域名 { reverse_proxy vikunja:80 }然后在compose文件里追加Caddy服务。Caddy最大的优点是不用手动搞证书它会自动申请、续期HTTPS证书反向代理配置也简洁到只有一行。它对家庭用户非常友好不用像Nginx那样写大段server块。注意这里的反代目标是vikunja:80也就是compose网络里的服务名而不用管容器内部的3456端口。新版全栈镜像已经在容器内部把API代理处理好了对外只暴露前端80端口即可。4.4 备份与恢复容器可以随便重建数据不能丢装好之后备份就是不得不谈的话题。任务数据往往比程序本身重要得多。备份主要分两块数据库和上传文件。PostgreSQL备份docker compose exec db pg_dump -U vikunja vikunja vikunja_backup.sql恢复时docker compose exec -T db psql -U vikunja vikunja vikunja_backup.sql文件目录备份就是打包命名卷里面Vikunja写入的文件内容路径通常在宿主机对应的volume目录或者直接备份vikunja_files卷。就我实践来看建议把pg_dump和文件打包放到一个脚本里用cron每天凌晨跑一次然后把备份文件同步到另一台设备或对象存储。这比任何容器高可用设置都靠谱。容器崩了随时重建备份丢了才真的是大事。5. 安装过程中最常见的报错和定位方法5.1 网页打不开端口映射、防火墙和容器日志的排查顺序容器启动后网页打不开是我在排查时遇到最多的情况。不要瞎猜按顺序检查docker ps先看容器状态如果STATUS显示Up说明容器在运行。接着在服务器本机测试curl http://127.0.0.1:3456本机能返回HTML页面内容但浏览器打不开那基本是云服务器的防火墙或安全组没有放行3456端口。登录云控制台在安全组规则里加一条TCP端口3456的入站规则。本机也打不开就要看容器日志docker logs vikunja --tail 50Vikunja的日志通常会把监听地址、端口、数据库连接状态都打印出来很多问题一目了然。一个容易忽略的点是端口被占用。如果3456端口被其他进程占了容器启动时端口映射会失败docker ps也会显示错误状态。换个端口映射比如-p 3457:80或者停掉占用进程即可。5.2 sqlite unable to open database file挂载目录权限和UID的坑第一次用docker run单容器方案时挂在文件目录后启动容器日志里报unable to open database file一度以为是容器内路径写错了。查了一圈才发现是挂载目录权限问题。Vikunja容器内进程以非root用户运行UID通常是1000。宿主机上/opt/vikunja/files目录如果是root创建、权限默认755容器内用户没有写入权限自然会报错。解决办法就是前面说的sudo chown -R 1000:1000 /opt/vikunja设置完重启容器问题解决。这类问题不算罕见很多自托管应用容器内都用非root用户运行陌生的镜像先看官方文档中关于USER或PUID/PGID的说明能省不少排查时间。5.3 外置数据库连接不上host该写db而不是localhost切到compose方案后常见的坑是把数据库连接地址写成localhost或127.0.0.1。在compose网络里每个服务名等同于一个主机名。Vikunja要连数据库应该填服务名db而不是本机回环地址。如果把VIKUNJA_DATABASE_HOST写成localhostVikunja容器内部找不到数据库服务日志会报连接失败。同理如果数据库跑在宿主机上而不是容器里Vikunja容器要访问宿主机数据库得填Docker虚拟网关地址通常是172.17.0.1而不是localhost。这一点对新手来说比较容易绕进去。出现数据库认证失败时先确认compose文件中的POSTGRES_USER、POSTGRES_PASSWORD和Vikunja这边的VIKUNJA_DATABASE_USER、VIKUNJA_DATABASE_PASSWORD完全一致。密码如果包含特殊字符注意环境变量里的转义问题。5.4 常见报错速查表报错/现象可能原因排查与解决Docker Desktop提示virtualization support not detectedBIOS未开启虚拟化开启VT-x/AMD-V启用WSL2功能permission denied while trying to connect to docker api用户不在docker组sudo usermod -aG docker $USER后重新登录镜像拉取慢或超时未配置镜像加速使用国内镜像加速地址配置后重启Docker容器Up但网页打不开安全组/防火墙拦截或端口映射异常本机curl验证检查云安全组入站规则sqlite unable to open database file挂载目录属主不是UID 1000chown -R 1000:1000挂载目录数据库连接失败Connection refused数据库地址写错或未就绪compose网络内写服务名db确认healthcheck通过password authentication failed for user vikunja数据库账号密码不一致核对两处环境变量6. 安装完成之后这几个设置我建议立刻做6.1 环境变量优先VK_前缀的使用习惯Vikunja支持环境变量配置也支持挂载配置文件config.yml。我在容器化部署时更习惯用环境变量因为compose文件本身就能承载全部配置不用额外准备配置文件也方便用git管理。Vikunja的环境变量都以VK_开头规则是把配置文件里的层级字段转成大写加下划线。比如配置文件里是database.host环境变量就是VIKUNJA_DATABASE_HOSTservice.jwt_secret对应VIKUNJA_SERVICE_JWT_SECRET。有一个变量强烈建议显式设置就是JWT密钥。如果不上配置Vikunja每次冷启动可能会生成新的临时密钥导致用户登录态丢失。在compose的environment里加一项固定的密钥比登录一次失效一次更省心。密钥可以用openssl rand -base64 32生成一串随机值这个值相对较长且难以猜测。6.2 注册策略、深色模式、附件大小这些开箱配置Vikunja默认开放用户注册。如果你部署在公网或者公司多人环境建议登录管理员后台把自助注册关掉改为邀请注册或管理员建号避免公网任意用户都能注册账号。界面侧Vikunja原生支持多主题和深色模式在个人设置里切一下就行不需要额外配置。附件上传默认限制大小如果团队协作中常用图片和文档附件建议根据实际需求调大上传上限。这个配置在环境变量里对应VK_FILES_MAX_SIZE单位是字节比如想允许50MB附件就写52428800。注意不要超出服务器或反向代理的请求体大小限制否则会表现为“上传失败”而不是明确的报错信息。6.3 日历订阅把任务日历同步到手机装好Vikunja后最值得长期使用的一个功能是任务日历订阅。在Vikunja项目或列表设置里可以找到iCal订阅链接。把这个链接添加到手机自带日历App或任意支持网络订阅的日历应用里任务截止日期就会自动出现在日历上双向勾选后也会同步回去。这个功能对普通人来说是很实用的任务清单平时看Vikunja到了具体哪天该做什么直接看日历就行不用在两个应用之间来回切。这也侧面说明了为什么部署时要记得挂载好数据目录、做好备份——日历订阅、附件、历史任务这些数据都是用得越久越有价值前期部署时把基础打牢后面才敢放心往里放真数据。我个人的经验是不要一上来就追求把Vikunja的所有功能都用满先跑通核心流程把任务清单和日历订阅用起来两周后再回过头优化主题、维护策略、调整备份频率。自托管工具的乐趣本就在这个逐步养成的过程中而这一步从Docker把它跑起来才算真正开始。