跳至主要内容

輕量部署(SQLite)

Aivory 的後端在啟動時按環境變數自動選型:DATABASE_URLpostgres:// 開頭就用 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 一個服務:去掉 postgresredisqdrant 三個服務及其 depends_on,把 DATABASE_URL 改成 SQLite 檔案路徑,並且不設定 REDIS_URLQDRANT_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/uploadsARTIFACT_DIR=/app/data/artifactsBACKUP_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 的能力不啟用;快取和限流計數隨程序重啟清零
qdrantRAG 全文注入回退知識庫仍可用,但不做向量相似度檢索,而是把範圍內的文件全文注入上下文;文件量大時消耗更多 token、檢索精度下降
元件可以按需保留

選型是各自獨立的:完全可以只把 Postgres 換成 SQLite,而保留 redisqdrant 服務(保留對應的 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 字元,否則應用拒絕啟動。

不要用去掉 AIVORY_ENV 的方式繞過校驗

開發環境下不設 JWT_SECRET 時,應用會自動生成一個隨機臨時金鑰,每次重啟所有登入會話全部失效,且這是為本機開發準備的行為,不是部署選項。任何對外提供服務的例項都應顯式設定 AIVORY_ENV: production 和強隨機 JWT_SECRET

備份

SQLite 模式的一大好處是備份面極小:所有持久化資料都在 DATA_DIR 一個目錄裡。

  • 首選:管理後臺匯出。Backup & Migration 頁面生成的全量備份 ZIP 是引擎中立的(manifest + 每表一個 JSONL + 可選檔案),不受"複製執行中的資料庫檔案"問題的影響,而且可以直接匯入到 Postgres 部署。
  • 冷備:先 docker compose stop,再整體複製 DATA_DIR 目錄。注意 aivory.db-walaivory.db-shm 是資料庫的一部分,不要在應用執行中只拷 aivory.db 單個檔案,WAL 中未合併的寫入會丟失,拷出來的檔案也可能不一致。

備份匯入的大小上限由 MAX_BACKUP_BYTES 控制(預設 20 GiB)。

與 Postgres 互遷

備份格式是引擎中立的:SQLite 備份可以匯入 Postgres 部署,反之亦然,序列和外部索引鍵由匯入過程自動處理。典型的"業務長大了,從 SQLite 升到完整棧"流程:

  1. 舊例項匯出:在管理後臺 Backup & Migration 生成全量備份(勾選檔案與向量,如有),下載 ZIP。
  2. 拉起新例項:按Docker Compose 生產部署啟動完整棧。首次啟動後,用與舊例項管理員相同的郵箱註冊首個賬號(它將成為管理員)。
  3. 匯入:在新例項的 Backup & Migration 頁面匯入 ZIP,輸入確認詞 REPLACE。匯入是整庫替換:在一個事務裡清空並重載所有表、恢復檔案與向量,完成後當前會話失效,需用備份裡的賬號密碼重新登入。
  4. 向量處理:如果舊例項沒有 Qdrant(本頁的最小部署就沒有),備份裡沒有向量資料。新例項可在管理後臺的向量維護功能裡從資料庫已存的 chunk 文本重建向量,無需原始檔案,但會消耗嵌入 API 呼叫。
為什麼管理員郵箱要一致

匯入完成後有一道防提權機制:除了"執行匯入操作的管理員郵箱"之外,備份中攜帶的其它 admin 賬號會被降級為普通使用者。新舊例項管理員郵箱一致,匯入後你的管理員身份才能原樣保留。

反方向(Postgres 遷到 SQLite,例如把小型部署收縮回單容器)流程完全相同,只是新例項換成本頁的最小 compose 檔案。

完整的匯出選項、非同步匯出任務、配置匯出與匯入等細節見備份與遷移

下一步