
把任務(wù)清單這種日常工具自托管起來是很多折騰過Docker的人都會動過的念頭。市面上Todo類應(yīng)用不少但數(shù)據(jù)在自己手里、界面能用、部署不復(fù)雜的開源方案我最后選了Vikunja。它用GoVue寫的一個Docker鏡像就能跑起來前五分鐘我就能在瀏覽器里看到登錄頁后面花時間反而不在安裝本身而是Docker環(huán)境、數(shù)據(jù)庫選型、數(shù)據(jù)持久化這些細枝末節(jié)。這篇文章就按我實際部署的順序?qū)懸槐閺腄ocker環(huán)境準(zhǔn)備、為什么選Vikunja、單容器快速跑通到compose編排數(shù)據(jù)庫和反向代理、再到安裝過程中最常踩的幾個報錯最后聊幾個裝完立刻要做的事。如果你正打算在服務(wù)器或自己電腦上裝一個隱私可控的待辦清單工具對Docker只停留在“聽說過”的層面這篇就是照著做就能跑通的實操記錄。1. 先搞定Docker環(huán)境這是攔路的第一道坎1.1 這個報錯不等于安裝失敗先看CPU虛擬化開關(guān)最近被問得最多的Docker問題不是Vikunja裝不上而是Docker Desktop壓根啟動不了報錯里帶著一句virtualization support was not detected。說實話這個問題跟Docker本身關(guān)系不大絕大多數(shù)情況是Windows的CPU虛擬化沒開或者WSL2功能沒啟用。判斷方法很簡單打開任務(wù)管理器切到“性能”選項卡看左下角的CPU信息里面有個“虛擬化”狀態(tài)。如果顯示“已禁用”直接關(guān)機進BIOS不同主板按鍵不同一般是Del或F2找Intel VT-x或AMD-V/SVM的開關(guān)打開后保存重啟。這一步做完再啟動Docker Desktop大概率就正常了。如果你確認(rèn)虛擬化已經(jīng)啟用但Docker Desktop還是報同樣的錯那要注意一下啟動方式。Docker Desktop在Windows上有兩種后端Hyper-V和WSL2。新版本默認(rèn)用WSL2這也是我推薦的選擇——鏡像啟動更快、內(nèi)存占用更可控。但WSL2本身需要在“啟用或關(guān)閉Windows功能”里把“適用于Linux的Windows子系統(tǒng)”勾上同時“虛擬機平臺”也要啟用。兩個功能都開重啟電腦再打開Docker Desktop就會好很多。1.2 Docker Desktop安裝完還要調(diào)兩個設(shè)置很多教程到安裝完就結(jié)束了其實裝完Docker Desktop后有兩個設(shè)置直接決定你后面順不順。第一個是設(shè)置里的“Resources”內(nèi)存分配。Docker Desktop默認(rèn)內(nèi)存可能是2GB跑Vikunja這種輕量應(yīng)用夠用但如果你同時跑數(shù)據(jù)庫、反代容器建議調(diào)到4GB以上。留意別把宿主機內(nèi)存全部給出去WSL2會動態(tài)占用但給得太多會讓W(xué)indows本身變卡。第二個是鏡像加速。國內(nèi)拉Docker官方鏡像經(jīng)常慢到懷疑人生尤其是第一次拉vikunja/vikunja這種上百MB的鏡像。解決辦法是找到Docker Desktop的“Docker Engine”配置JSON加入一個國內(nèi)鏡像加速地址不同云廠商提供的加速器地址不一樣找一個在你網(wǎng)絡(luò)環(huán)境下速度穩(wěn)定的就行。配置完重啟Docker Desktop再拉鏡像速度會上來一大截?!皢觗ocker服務(wù)失敗”這類報錯之前見過不少大多是Docker Desktop安裝過程中Windows服務(wù)沒有注冊成功或者跟殺毒軟件有沖突。修復(fù)路徑基本是卸載重裝Docker Desktop裝完后把Docker相關(guān)的幾個Windows服務(wù)狀態(tài)確認(rèn)一遍必要時重啟電腦。1.3 Linux服務(wù)器上的dockersock權(quán)限問題如果你是在云服務(wù)器上跑Docker而不是Windows桌面那遇到的第一個報錯大概率是這種permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock這個錯誤特別容易讓新手以為是Docker服務(wù)沒啟動其實不是。它只是因為當(dāng)前用戶不在docker用戶組里沒有權(quán)限訪問Docker的socket文件。解決方法sudo usermod -aG docker $USER newgrp docker docker run hello-world第一行把自己加進docker組第二行讓用戶組立刻生效第三行驗證。如果還是不行確認(rèn)一下當(dāng)前的Docker服務(wù)狀態(tài)sudo systemctl status docker服務(wù)狀態(tài)是active就可以放心往下走了。2. 為什么我選Vikunja以及它和純Docker單容器方案的匹配點2.1 待辦工具那么多選Vikunja的三個理由選Vikunja之前我試過幾類方案在線網(wǎng)站的看板工具確實漂亮但任務(wù)數(shù)據(jù)全部放在別人服務(wù)器上一旦免費額度收緊或賬號出問題數(shù)據(jù)很難干凈導(dǎo)出用文本文件做清單又太原始手機上沒辦法快速查看和勾選。Vikunja正好在兩者之間找到了平衡點開源、可以完全自托管界面卻是現(xiàn)代Web應(yīng)用的水準(zhǔn)。它的核心能力覆蓋了一個人能用到、小團隊也夠用的幾乎所有場景支持列表視圖、看板視圖、甘特圖視圖和表格視圖而看板模式下拖拽操作流暢度相當(dāng)高任務(wù)可以設(shè)置優(yōu)先級、截止時間、重復(fù)周期和標(biāo)簽還能指派給團隊成員每個列表、每個項目都能生成日歷訂閱鏈接可以在手機日歷里看到截止日期支持PWA方式添加到手機主屏用起來接近原生App最關(guān)鍵的是Vikunja不依賴重型第三方服務(wù)一個Docker容器加上可選的數(shù)據(jù)庫就能完整運行。這正是我想要的“能自己掌控一切”的部署方式。2.2 一個容器還是兩個容器先讀懂Vikunja的架構(gòu)網(wǎng)上搜Vikunja的安裝教程能看到兩種完全不同的說法。一種說拉兩個鏡像一個后端API加一個前端頁面部署時要配反代、處理跨域另一種說一個鏡像直接跑起來。兩種說法都沒錯但屬于不同時期的架構(gòu)。Vikunja的官方鏡像在較新的版本里已經(jīng)把后端Go寫的API服務(wù)和前端Vue構(gòu)建的頁面打包在一起了。現(xiàn)在直接從Docker Hub拉vikunja/vikunja這個鏡像一個容器就能同時提供前端頁面和API接口不再需要單獨搞兩個容器。這個小細節(jié)很重要。很多老教程還在按“兩個鏡像”的思路寫照著做不是不行但多了一堆不必要的配置項對新手很不友好。我下面的操作都是基于新版全棧鏡像容器內(nèi)部前端頁面監(jiān)聽80端口API服務(wù)在容器內(nèi)占用3456端口對外我們只需要暴露一個80端口就行。2.3 數(shù)據(jù)放哪SQLite和PostgreSQL的選擇邏輯Vikunja支持三種數(shù)據(jù)存儲方式內(nèi)置SQLite、PostgreSQL、MySQL/MariaDB。很多第一次部署的人會在這里糾結(jié)其實選擇邏輯很直接。如果只是個人使用任務(wù)量不大、并發(fā)很低直接用內(nèi)置SQLite是最省事的——不用額外管理數(shù)據(jù)庫容器一個鏡像把所有事情都干了。但要注意SQLite模式下數(shù)據(jù)庫文件是落在容器里的必須把數(shù)據(jù)目錄持久化出來否則容器一重建任務(wù)全丟。如果是多人使用或者部署在公網(wǎng)服務(wù)器上建議直接用PostgreSQL。PostgreSQL在并發(fā)寫入、備份恢復(fù)、數(shù)據(jù)安全性上都明顯優(yōu)于SQLite而且后續(xù)做定時備份用pg_dump也順手。我的建議是不管什么場景既然都用了Docker多跑一個PostgreSQL容器并不復(fù)雜踏踏實實把數(shù)據(jù)放在外置數(shù)據(jù)庫里省得以后再遷移。后面我會給出完整的compose配置。3. 用docker run快速跑起來先讓界面出現(xiàn)在瀏覽器里3.1 最簡啟動命令與參數(shù)逐項拆解先把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拆開看每個參數(shù)的意思-d后臺運行不占用當(dāng)前終端--name vikunja給容器起名字后續(xù)docker logs vikunja、docker restart vikunja都靠這個名字-p 3456:80宿主機3456端口映射到容器內(nèi)80端口瀏覽器訪問http://服務(wù)器IP:3456-v /opt/vikunja/files:/app/vikunja/files把容器內(nèi)附件、上傳文件目錄掛載到宿主機-v /opt/vikunja/db:/app/vikunja/db把SQLite數(shù)據(jù)庫文件目錄掛載出來這個掛載至關(guān)重要--restart unless-stopped服務(wù)器重啟或Docker重啟時自動拉起容器這段命令里db目錄的掛載很容易被忽略。Vikunja用內(nèi)置SQLite時數(shù)據(jù)庫文件就存放在這個目錄里。只掛載files目錄不做db掛載的話容器重建時任務(wù)數(shù)據(jù)會全部歸零。3.2 首次登錄、初始化管理員和立刻要做的安全設(shè)置命令執(zhí)行完后等幾秒鐘讓容器完成啟動然后用瀏覽器訪問http://你的服務(wù)器IP:3456首次打開是Vikunja的登錄頁面。默認(rèn)管理員賬號是admin密碼也是admin。第一次進入后立刻去用戶設(shè)置里把密碼改掉同時把個人頭像、時區(qū)、默認(rèn)語言設(shè)置好。之所以強調(diào)這一步是因為如果你把服務(wù)映射到了公網(wǎng)默認(rèn)的admin密碼等于把大門敞開了。哪怕是個人NAS或者內(nèi)網(wǎng)部署養(yǎng)成改默認(rèn)密碼的習(xí)慣也很有必要。登錄進去之后可以先建一個測試列表隨便加兩個任務(wù)試試。到這里Vikunja已經(jīng)跑通了你已經(jīng)擁有了一個完全自托管的待辦清單。3.3 單容器方案的兩個數(shù)據(jù)持久化坑docker run跑通之后有兩件事不要急著跳過。第一確認(rèn)掛載目錄的屬主權(quán)限。Vikunja容器內(nèi)默認(rèn)以固定的UID運行通常是1000如果宿主機掛載目錄的所有者是root或其他用戶容器寫入數(shù)據(jù)時可能會報sqlite unable to open database file。保險起見創(chuàng)建目錄后執(zhí)行一下sudo mkdir -p /opt/vikunja/files /opt/vikunja/db sudo chown -R 1000:1000 /opt/vikunja第二改密碼、改配置之前想清楚。用docker run方式啟動后如果要改配置項通常是用-e環(huán)境變量但每次新增環(huán)境變量都要重新創(chuàng)建容器。這時候你會發(fā)現(xiàn)用compose管理配置要方便得多。所以快速體驗結(jié)束之后接下來就應(yīng)該轉(zhuǎn)入compose方案。4. 上生產(chǎn)docker compose 編排數(shù)據(jù)庫和反向代理4.1 compose 文件直接抄全棧鏡像 PostgreSQL真正穩(wěn)定運行我推薦用Docker Compose。下面這個compose文件我直接貼在服務(wù)器上就能用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 # 把上面內(nèi)容保存成 docker-compose.yml docker compose up -d等待鏡像拉取完成后同樣訪問http://服務(wù)器IP:3456看到登錄頁就說明compose方案也跑通了。4.2 我為什么建議你給Vikunja配獨立數(shù)據(jù)庫而不是一直用SQLite有人會問既然SQLite也能跑為什么還要多加一個PostgreSQL容器我個人的體會是這取決于“任務(wù)數(shù)據(jù)”對你的價值。待辦清單里的任務(wù)可能包含了近期工作安排、家庭事項、項目計劃一旦損壞或丟失找回的代價遠大于多維護一個容器的成本。PostgreSQL在幾個方面比SQLite更適合作為長期數(shù)據(jù)層備份恢復(fù)更成熟pg_dump可以一邊運行一邊做邏輯備份不用停服務(wù)并發(fā)寫入更穩(wěn)健多人同時創(chuàng)建任務(wù)、勾選任務(wù)時不會出現(xiàn)鎖等待數(shù)據(jù)庫文件獨立存在命名卷里容器升級、鏡像重拉本質(zhì)上不影響數(shù)據(jù)compose里那個healthcheck也很關(guān)鍵。它保證數(shù)據(jù)庫完全就緒后Vikunja才開始連接。沒有這一步經(jīng)常出現(xiàn)Vikunja先啟動、數(shù)據(jù)庫還沒打開端口最后報連接失敗的情況。4.3 用Caddy把Vikunja放到域名下并自動HTTPS如果只用IP加端口訪問到這一步就可以停了。但如果你有自己的域名想讓Vikunja跑在標(biāo)準(zhǔn)443端口、自動帶HTTPS加一個Caddy容器是最省力的方式。新建一個caddy目錄放配置你的域名 { reverse_proxy vikunja:80 }然后在compose文件里追加Caddy服務(wù)。Caddy最大的優(yōu)點是不用手動搞證書它會自動申請、續(xù)期HTTPS證書反向代理配置也簡潔到只有一行。它對家庭用戶非常友好不用像Nginx那樣寫大段server塊。注意這里的反代目標(biāo)是vikunja:80也就是compose網(wǎng)絡(luò)里的服務(wù)名而不用管容器內(nèi)部的3456端口。新版全棧鏡像已經(jīng)在容器內(nèi)部把API代理處理好了對外只暴露前端80端口即可。4.4 備份與恢復(fù)容器可以隨便重建數(shù)據(jù)不能丟裝好之后備份就是不得不談的話題。任務(wù)數(shù)據(jù)往往比程序本身重要得多。備份主要分兩塊數(shù)據(jù)庫和上傳文件。PostgreSQL備份docker compose exec db pg_dump -U vikunja vikunja vikunja_backup.sql恢復(fù)時docker compose exec -T db psql -U vikunja vikunja vikunja_backup.sql文件目錄備份就是打包命名卷里面Vikunja寫入的文件內(nèi)容路徑通常在宿主機對應(yīng)的volume目錄或者直接備份vikunja_files卷。就我實踐來看建議把pg_dump和文件打包放到一個腳本里用cron每天凌晨跑一次然后把備份文件同步到另一臺設(shè)備或?qū)ο蟠鎯?。這比任何容器高可用設(shè)置都靠譜。容器崩了隨時重建備份丟了才真的是大事。5. 安裝過程中最常見的報錯和定位方法5.1 網(wǎng)頁打不開端口映射、防火墻和容器日志的排查順序容器啟動后網(wǎng)頁打不開是我在排查時遇到最多的情況。不要瞎猜按順序檢查docker ps先看容器狀態(tài)如果STATUS顯示Up說明容器在運行。接著在服務(wù)器本機測試curl http://127.0.0.1:3456本機能返回HTML頁面內(nèi)容但瀏覽器打不開那基本是云服務(wù)器的防火墻或安全組沒有放行3456端口。登錄云控制臺在安全組規(guī)則里加一條TCP端口3456的入站規(guī)則。本機也打不開就要看容器日志docker logs vikunja --tail 50Vikunja的日志通常會把監(jiān)聽地址、端口、數(shù)據(jù)庫連接狀態(tài)都打印出來很多問題一目了然。一個容易忽略的點是端口被占用。如果3456端口被其他進程占了容器啟動時端口映射會失敗docker ps也會顯示錯誤狀態(tài)。換個端口映射比如-p 3457:80或者停掉占用進程即可。5.2 sqlite unable to open database file掛載目錄權(quán)限和UID的坑第一次用docker run單容器方案時掛在文件目錄后啟動容器日志里報unable to open database file一度以為是容器內(nèi)路徑寫錯了。查了一圈才發(fā)現(xiàn)是掛載目錄權(quán)限問題。Vikunja容器內(nèi)進程以非root用戶運行UID通常是1000。宿主機上/opt/vikunja/files目錄如果是root創(chuàng)建、權(quán)限默認(rèn)755容器內(nèi)用戶沒有寫入權(quán)限自然會報錯。解決辦法就是前面說的sudo chown -R 1000:1000 /opt/vikunja設(shè)置完重啟容器問題解決。這類問題不算罕見很多自托管應(yīng)用容器內(nèi)都用非root用戶運行陌生的鏡像先看官方文檔中關(guān)于USER或PUID/PGID的說明能省不少排查時間。5.3 外置數(shù)據(jù)庫連接不上host該寫db而不是localhost切到compose方案后常見的坑是把數(shù)據(jù)庫連接地址寫成localhost或127.0.0.1。在compose網(wǎng)絡(luò)里每個服務(wù)名等同于一個主機名。Vikunja要連數(shù)據(jù)庫應(yīng)該填服務(wù)名db而不是本機回環(huán)地址。如果把VIKUNJA_DATABASE_HOST寫成localhostVikunja容器內(nèi)部找不到數(shù)據(jù)庫服務(wù)日志會報連接失敗。同理如果數(shù)據(jù)庫跑在宿主機上而不是容器里Vikunja容器要訪問宿主機數(shù)據(jù)庫得填Docker虛擬網(wǎng)關(guān)地址通常是172.17.0.1而不是localhost。這一點對新手來說比較容易繞進去。出現(xiàn)數(shù)據(jù)庫認(rèn)證失敗時先確認(rèn)compose文件中的POSTGRES_USER、POSTGRES_PASSWORD和Vikunja這邊的VIKUNJA_DATABASE_USER、VIKUNJA_DATABASE_PASSWORD完全一致。密碼如果包含特殊字符注意環(huán)境變量里的轉(zhuǎn)義問題。5.4 常見報錯速查表報錯/現(xiàn)象可能原因排查與解決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后重新登錄鏡像拉取慢或超時未配置鏡像加速使用國內(nèi)鏡像加速地址配置后重啟Docker容器Up但網(wǎng)頁打不開安全組/防火墻攔截或端口映射異常本機curl驗證檢查云安全組入站規(guī)則sqlite unable to open database file掛載目錄屬主不是UID 1000chown -R 1000:1000掛載目錄數(shù)據(jù)庫連接失敗Connection refused數(shù)據(jù)庫地址寫錯或未就緒compose網(wǎng)絡(luò)內(nèi)寫服務(wù)名db確認(rèn)healthcheck通過password authentication failed for user vikunja數(shù)據(jù)庫賬號密碼不一致核對兩處環(huán)境變量6. 安裝完成之后這幾個設(shè)置我建議立刻做6.1 環(huán)境變量優(yōu)先VK_前綴的使用習(xí)慣Vikunja支持環(huán)境變量配置也支持掛載配置文件config.yml。我在容器化部署時更習(xí)慣用環(huán)境變量因為compose文件本身就能承載全部配置不用額外準(zhǔn)備配置文件也方便用git管理。Vikunja的環(huán)境變量都以VK_開頭規(guī)則是把配置文件里的層級字段轉(zhuǎn)成大寫加下劃線。比如配置文件里是database.host環(huán)境變量就是VIKUNJA_DATABASE_HOSTservice.jwt_secret對應(yīng)VIKUNJA_SERVICE_JWT_SECRET。有一個變量強烈建議顯式設(shè)置就是JWT密鑰。如果不上配置Vikunja每次冷啟動可能會生成新的臨時密鑰導(dǎo)致用戶登錄態(tài)丟失。在compose的environment里加一項固定的密鑰比登錄一次失效一次更省心。密鑰可以用openssl rand -base64 32生成一串隨機值這個值相對較長且難以猜測。6.2 注冊策略、深色模式、附件大小這些開箱配置Vikunja默認(rèn)開放用戶注冊。如果你部署在公網(wǎng)或者公司多人環(huán)境建議登錄管理員后臺把自助注冊關(guān)掉改為邀請注冊或管理員建號避免公網(wǎng)任意用戶都能注冊賬號。界面?zhèn)萔ikunja原生支持多主題和深色模式在個人設(shè)置里切一下就行不需要額外配置。附件上傳默認(rèn)限制大小如果團隊協(xié)作中常用圖片和文檔附件建議根據(jù)實際需求調(diào)大上傳上限。這個配置在環(huán)境變量里對應(yīng)VK_FILES_MAX_SIZE單位是字節(jié)比如想允許50MB附件就寫52428800。注意不要超出服務(wù)器或反向代理的請求體大小限制否則會表現(xiàn)為“上傳失敗”而不是明確的報錯信息。6.3 日歷訂閱把任務(wù)日歷同步到手機裝好Vikunja后最值得長期使用的一個功能是任務(wù)日歷訂閱。在Vikunja項目或列表設(shè)置里可以找到iCal訂閱鏈接。把這個鏈接添加到手機自帶日歷App或任意支持網(wǎng)絡(luò)訂閱的日歷應(yīng)用里任務(wù)截止日期就會自動出現(xiàn)在日歷上雙向勾選后也會同步回去。這個功能對普通人來說是很實用的任務(wù)清單平時看Vikunja到了具體哪天該做什么直接看日歷就行不用在兩個應(yīng)用之間來回切。這也側(cè)面說明了為什么部署時要記得掛載好數(shù)據(jù)目錄、做好備份——日歷訂閱、附件、歷史任務(wù)這些數(shù)據(jù)都是用得越久越有價值前期部署時把基礎(chǔ)打牢后面才敢放心往里放真數(shù)據(jù)。我個人的經(jīng)驗是不要一上來就追求把Vikunja的所有功能都用滿先跑通核心流程把任務(wù)清單和日歷訂閱用起來兩周后再回過頭優(yōu)化主題、維護策略、調(diào)整備份頻率。自托管工具的樂趣本就在這個逐步養(yǎng)成的過程中而這一步從Docker把它跑起來才算真正開始。