docker-compose.yml
Rama main · réplica de lectura del repositorio original.
services:
app:
# 發版時 release.yml 推的映像(linux/amd64、linux/arm64),標籤取自 .env 的 KNOTEBOOK_VERSION。
# Release 附件版的這個檔由 release.yml 產生:預設標籤換成該版版號、拿掉 build:(只能 pull)。
# 刻意不設 pull_policy——compose 的預設行為正是要的:
# - `docker compose up -d`:本機沒有這個映像才 pull;有 build: 時 pull 失敗會改從原始碼 build。
# - `docker compose pull`:一律重拉。明設 pull_policy: missing 的話,本機已有的標籤(例如 latest)
# 會被跳過,升級拉不到新版(Compose v2.27 實測)。
# - `docker compose up -d --build`(repo 裡):一律從原始碼 build(不 pull),產物標成這個映像名。
image: ghcr.io/sw-willie-wu/knotebook:${KNOTEBOOK_VERSION:-latest}
build:
context: .
dockerfile: docker/Dockerfile
env_file: .env
# 日誌輪替上限(issue #50 的治標半):docker 的 json-file driver 預設**沒有**大小
# 上限,任何能讓 app 持續寫日誌的路徑(不只 #50 那條,未來的都算)最終症狀都是
# 自架者的磁碟被灌爆。50MB × 5 檔對排錯綽綽有餘;治本(對灌日誌的連線斷線)在
# collab/server.ts 的訊息預檢。
logging:
driver: json-file
options:
max-size: 50m
max-file: "5"
# 選配:限制 app 能用的 CPU 與記憶體(#398)。設了之後,沒設 PARSE_WORKERS 時 server 會照這裡的上限
# (容器的 cgroup)推算解析 worker 數:min(CPU − 1,最多 4;floor(記憶體 × 0.5 ÷ PARSE_WORKER_MEMORY_MB)),至少 1。
# 不設就照主機的 CPU 與記憶體算。怎麼挑數字見 docs/self-hosting.md「Parse workers」。
# deploy:
# resources:
# limits:
# cpus: "2"
# memory: 4g
# 預設只綁 loopback:public 拓撲(docs/self-hosting.md「Deployment prerequisites」)的假設是
# app 只透過反代對外曝露;若直接 "3000:3000"(等同 0.0.0.0:3000),docker 會
# 直接在 iptables 開洞,繞過 host 防火牆規則,讓區網/公網能不經反代直接打到
# app。Quickstart 的 `curl http://localhost:3000/...` 不受影響(127.0.0.1 對本機
# 而言就是 localhost)。要用 trusted-LAN plain-http 拓撲,改用下面註解掉的替代寫法。
ports:
- "127.0.0.1:3000:3000"
# LAN mode(trusted-LAN plain-http,見 docs/self-hosting.md「Deployment prerequisites」):
# 換成 "3000:3000" 會把 app 直接曝露到你的網段(等同 0.0.0.0:3000),並透過
# docker 自己在 iptables 開的洞繞過 host 防火牆規則——啟用前務必先讀過
# docs/self-hosting.md 的風險清單(cleartext 密碼/session cookie、無 Secure flag)。
# - "3000:3000"
# 上傳圖片持久化——不掛 volume 的話,圖片只存在 container 的可寫層,
# `docker compose down`/重建 container 會整批遺失。掛載點與 Dockerfile 建的
# `/app/uploads`(已 chown 給 node)一致,`src/index.ts` 用同一個相對路徑
# (`process.cwd()/uploads`,WORKDIR 為 /app)解析。
volumes:
- uploads:/app/uploads
# 讓 DATABASE_URL 能用 host.docker.internal 指到「跑 docker 的這台主機」上的 PostgreSQL
# (Linux 的 docker 預設不提供這個名字)。用內建 db 時用不到,留著無害。見
# docs/self-hosting.md「Using your own PostgreSQL」。
extra_hosts:
- "host.docker.internal:host-gateway"
# BUNDLED_DB=0(不起內建 db,見下方 db 的 scale)時 compose 不等這個依賴:scale 為 0 的依賴
# 不等是 Docker Compose v2.3.0 起的有意行為(`shouldWaitForDependency` 的 `GetScale() == 0`
# 分支,commit 09e0fa94;v2.27 實測同),所以 BUNDLED_DB 需要 Compose v2.3.0 以上。這裡不必寫
# required: false——寫了的話,內建 db 起不來時 app 也會照樣被啟動,預設起法的失敗行為就變了。
depends_on:
db:
condition: service_healthy
healthcheck:
# 用 127.0.0.1 而非 localhost:避免容器內 /etc/hosts 或 Node DNS 解析(部分
# image 的 getaddrinfo 在無 IPv6 時可能先嘗試 ::1)導致的額外延遲/失敗,
# 明確指定 loopback IPv4 位址。
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/healthz').then(r=>process.exit(r.ok?0:1),()=>process.exit(1))"]
interval: 5s
timeout: 3s
retries: 5
start_period: 10s
restart: unless-stopped
db:
image: pgvector/pgvector:pg17
# 內建 PostgreSQL:預設會起(BUNDLED_DB 未設=1)。改用自己的 PostgreSQL 時在 .env 設
# BUNDLED_DB=0、並把 DATABASE_URL 指過去——這個容器就不會被建立,已在跑的會被移除
# (db_data volume 保留)。只接受 0 或 1:非數字插值失敗、負數讓 compose panic、大於 1 會讓
# 多個 postgres 共用同一個 db_data。
scale: ${BUNDLED_DB:-1}
environment:
POSTGRES_USER: knotebook
# 可用 shell/.env 的 POSTGRES_PASSWORD 覆寫預設值;注意 Postgres 只在「第一次
# 初始化空 data 目錄」時套用這個變數(見 docs/known-limitations.md),事後改這裡
# 不會自動生效。
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-knotebook}
POSTGRES_DB: knotebook
volumes:
- db_data:/var/lib/postgresql/data
healthcheck:
# CMD 陣列形(不經 shell)、走 TCP:
# - CMD-SHELL 形會讓 sh 留著當 pg_isready 的父程序;逾時時 docker 只殺 sh,孤兒 pg_isready
# 被 PID 1 的 postmaster 收養,之後以 exit 2 結束會被當成 backend 崩潰、整座 cluster 重啟。
# - 走 unix socket 的話,第一次初始化(initdb)期間那個只聽 socket 的暫時 server 會讓它提早
# 回報就緒;走 TCP 要等正式 server 起來才通。
# timeout 5s 大於 pg_isready 自己的 3 秒連線逾時,讓它自己結束。start_period 60s:首次初始化
# 較慢的機器在這段期間的失敗不計入 retries,一成功就 healthy,所以快的機器不會因此等更久。
test: ["CMD", "pg_isready", "--host", "127.0.0.1", "--username", "knotebook", "--dbname", "knotebook"]
interval: 5s
timeout: 5s
retries: 5
start_period: 60s
restart: unless-stopped
volumes:
db_data:
uploads: