輕量部署(SQLite)
Aivory 的後端在啟動時按環境變數自動選型:DATABASE_URL 以 postgres:// 開頭就用 PostgreSQL,否則(例如一個 .db 檔案路徑)使用內嵌 SQLite;REDIS_URL 留空則用程序內快取/佇列;QDRANT_URL 留空則停用向量檢索,RAG 回退為注入全文上下文。生產二進位制和開發用的是同一個,SQLite 驅動已經編譯在映象裡。
這意味著你可以把完整生產棧的 5 個服務砍到 1 個:只跑 app 容器,資料落在一個 SQLite 檔案裡。本頁講清楚這種部署方式適合誰、compose 怎麼改、有什麼限制,以及日後如何無痛遷移到 Postgres。
何時選擇 SQLite 模式
| 場景 | 推薦 |
|---|---|
| 個人使用 / 試用評估 | SQLite,一個容器一分鐘拉起 |
| 小團隊(個位數到十幾人),單機部署 | SQLite 通常夠用 |
| 資源受限的小 VPS(1 核 1 GB 級別) | SQLite,完整棧跑不動 |
| 需要多副本 / 水平擴充套件 | 必須 Postgres,SQLite 是單寫入者 |
| 寫入密集(大量併發對話、頻繁知識庫寫入) | 建議 Postgres |
| 需要外部工具直接併發讀寫資料庫 | 建議 Postgres |
選 SQLite 不是一錘子買賣:備份格式是引擎中立的,之後隨時可以整庫遷到 Postgres(見下文)。
最小 compose 檔案
只保留 app 一個服務:去掉 postgres、redis、qdrant 三個服務及其 depends_on,把 DATABASE_URL 改成 SQLite 檔案路徑,並且不設定 REDIS_URL 和 QDRANT_URL。
name: aivory
services:
app:
image: ghcr.io/${IMAGE_OWNER:-hjxwz123}/aivory-app:${IMAGE_TAG:-latest}
restart: unless-stopped
ports:
- "80:8787"
environment:
AIVORY_ENV: production
# SQLite:非 postgres:// 的值即選中內嵌 SQLite。
# 兩個 pragma 分別開啟 WAL 日誌模式和 5 秒寫鎖等待。
DATABASE_URL: "/app/data/aivory.db?_pragma=journal_mode(WAL)&_pragma=busy_timeout(5000)"
# 不設定 REDIS_URL:使用程序內快取/佇列
# 不設定 QDRANT_URL:停用向量檢索,RAG 走全文注入回退
JWT_SECRET: ${JWT_SECRET:?set JWT_SECRET in .env}
# 試用時可置 true,啟用內建演示模型,無需任何真實 API key
ENABLE_MOCK_PROVIDER: ${ENABLE_MOCK_PROVIDER:-false}
volumes:
# 一個目錄裝下所有持久化資料:aivory.db + uploads/ + artifacts/ + backups/
- ${DATA_DIR:-./data}:/app/data
配套的 .env 只需要一行必填項:
# openssl rand -hex 32
JWT_SECRET=<至少 32 字元的強隨機值>
啟動與驗證:
docker compose up -d
curl -fsS http://localhost/api/health
首次啟動時系統沒有任何使用者,開啟站點後第一個註冊的賬號自動成為管理員,詳見首次執行配置。
為什麼資料庫檔案放在 /app/data 下
映象內建的上傳目錄、產物目錄和備份目錄都在 /app/data 下(UPLOAD_DIR=/app/data/uploads、ARTIFACT_DIR=/app/data/artifacts、BACKUP_DIR=/app/data/backups)。把 SQLite 檔案也放進 /app/data,一個繫結掛載就覆蓋了全部持久化資料:
./data/ # 宿主機目錄(DATA_DIR)
├── aivory.db # SQLite 主庫
├── aivory.db-wal # WAL 日誌(執行期存在,屬於資料庫的一部分)
├── aivory.db-shm # WAL 共享記憶體檔案
├── uploads/ # 使用者上傳的檔案
├── artifacts/ # 生成的產物
└── backups/ # 管理後臺匯出的備份歸檔
如果 DATABASE_URL 指向掛載卷之外的容器內路徑,容器一旦重建,整個資料庫就沒了。服務端不設 DATABASE_URL 時的預設值是 ./data/aivory.db?_pragma=journal_mode(WAL)&_pragma=busy_timeout(5000)(相對程序工作目錄,在容器內即 /app/data/ 下),恰好落在掛載點內;顯式寫成絕對路徑是為了杜絕歧義。
兩個 pragma 引數的含義
| 引數 | 值 | 作用 |
|---|---|---|
_pragma=journal_mode(WAL) | WAL | 寫前日誌模式:讀寫不互斥,多個讀者可與單個寫者併發 |
_pragma=busy_timeout(5000) | 5000 ms | 遇到寫鎖時等待最多 5 秒再報錯,而不是立即失敗 |
這兩個引數就是服務端的預設配置,照抄即可,不建議刪減。
砍掉 Redis 和 Qdrant 的代價
| 去掉的元件 | 替代行為 | 實際影響 |
|---|---|---|
postgres | 內嵌 SQLite | 單寫入者限制(見下節);功能無差異 |
redis | 程序內快取與佇列 | 單例項下功能可用;流恢復等依賴 Redis 的能力不啟用;快取和限流計數隨程序重啟清零 |
qdrant | RAG 全文注入回退 | 知識庫仍可用,但不做向量相似度檢索,而是把範圍內的文件全文注入上下文;文件量大時消耗更多 token、檢索精度下降 |
選型是各自獨立的:完全可以只把 Postgres 換成 SQLite,而保留 redis 和 qdrant 服務(保留對應的 REDIS_URL / QDRANT_URL / QDRANT_API_KEY 環境變數和 depends_on 條目即可)。知識庫是重度使用場景的話,建議至少保留 Qdrant。
程式碼沙箱怎麼辦
沙箱與資料庫選型無關。需要程式碼執行功能時,把完整棧裡的 sandbox 服務原樣搬進你的 compose 檔案,並給 app 加上兩個環境變數:
environment:
# ...上面的變數保持不變...
SANDBOX_BASE_URL: "http://sandbox:8000"
SANDBOX_API_KEY: ${SANDBOX_API_KEY:-aivory-bundled-sandbox}
沙箱服務的完整定義和安全注意事項(Docker socket 掛載、不釋出埠等)見程式碼沙箱部署。不需要程式碼執行就什麼都不加,應用其餘功能不受影響。
單寫入者限制
SQLite 是單檔案嵌入式資料庫,WAL 模式下允許多個讀者與一個寫者併發。這帶來幾條硬性約束:
- 只能有一個
app例項開啟這個資料庫檔案。不要把服務擴充套件為多副本,不要讓兩個容器掛同一個資料目錄,也不要在應用執行時用其它工具對該檔案做寫操作。 - 資料庫檔案必須在本地檔案系統上。不要把
DATA_DIR放在 NFS 或其它網路檔案系統上,檔案鎖語義不可靠,可能導致資料庫損壞。 - 寫入吞吐有上限。
busy_timeout(5000)意味著併發寫高峰時,後到的寫請求最多排隊 5 秒;持續的寫入壓力堆積會表現為請求變慢甚至超時。到了這個階段就該遷 Postgres 了。
JWT_SECRET 在 SQLite 下同樣強制
不要以為"輕量部署"就能省掉這一項。判定是否執行部署級安全校驗看的是:AIVORY_ENV 是否為非開發值,或 DATABASE_URL 是否為 Postgres。上面的最小 compose 設定了 AIVORY_ENV: production,所以即使資料庫是 SQLite,JWT_SECRET 也必須設定且至少 32 字元,否則應用拒絕啟動。
開發環境下不設 JWT_SECRET 時,應用會自動生成一個隨機臨時金鑰,每次重啟所有登入會話全部失效,且這是為本機開發準備的行為,不是部署選項。任何對外提供服務的例項都應顯式設定 AIVORY_ENV: production 和強隨機 JWT_SECRET。
備份
SQLite 模式的一大好處是備份面極小:所有持久化資料都在 DATA_DIR 一個目錄裡。
- 首選:管理後臺匯出。Backup & Migration 頁面生成的全量備份 ZIP 是引擎中立的(manifest + 每表一個 JSONL + 可選檔案),不受"複製執行中的資料庫檔案"問題的影響,而且可以直接匯入到 Postgres 部署。
- 冷備:先
docker compose stop,再整體複製DATA_DIR目錄。注意aivory.db-wal和aivory.db-shm是資料庫的一部分,不要在應用執行中只拷aivory.db單個檔案,WAL 中未合併的寫入會丟失,拷出來的檔案也可能不一致。
備份匯入的大小上限由 MAX_BACKUP_BYTES 控制(預設 20 GiB)。
與 Postgres 互遷
備份格式是引擎中立的:SQLite 備份可以匯入 Postgres 部署,反之亦然,序列和外部索引鍵由匯入過程自動處理。典型的"業務長大了,從 SQLite 升到完整棧"流程:
- 舊例項匯出:在管理後臺 Backup & Migration 生成全量備份(勾選檔案與向量,如有),下載 ZIP。
- 拉起新例項:按Docker Compose 生產部署啟動完整棧。首次啟動後,用與舊例項管理員相同的郵箱註冊首個賬號(它將成為管理員)。
- 匯入:在新例項的 Backup & Migration 頁面匯入 ZIP,輸入確認詞
REPLACE。匯入是整庫替換:在一個事務裡清空並重載所有表、恢復檔案與向量,完成後當前會話失效,需用備份裡的賬號密碼重新登入。 - 向量處理:如果舊例項沒有 Qdrant(本頁的最小部署就沒有),備份裡沒有向量資料。新例項可在管理後臺的向量維護功能裡從資料庫已存的 chunk 文本重建向量,無需原始檔案,但會消耗嵌入 API 呼叫。
匯入完成後有一道防提權機制:除了"執行匯入操作的管理員郵箱"之外,備份中攜帶的其它 admin 賬號會被降級為普通使用者。新舊例項管理員郵箱一致,匯入後你的管理員身份才能原樣保留。
反方向(Postgres 遷到 SQLite,例如把小型部署收縮回單容器)流程完全相同,只是新例項換成本頁的最小 compose 檔案。
完整的匯出選項、非同步匯出任務、配置匯出與匯入等細節見備份與遷移。