SSD Nodes Learn 🎉 VPS $5.50/月起
指南 Matt Connor作者: Matt Connor · 已更新 2026-08-13

AFFiNE 自架教學:Docker Compose 與備份

用 Docker Compose 在單一 VPS 自架 AFFiNE,了解 4 個容器、固定 image tags、資料位置與備份方式,以及 2 GB RAM 實際能支援的規模。

自架 AFFiNE 的內容

自架 AFFiNE 後,您會在自己管理的伺服器上取得類似 Notion 的工作區,並以 4 個容器執行:應用程式、一次性 migration job、Postgres 及 Redis。系統內建即時協作功能;自架工作區預設最多可容納 10 個席位。安裝只需要 1 個 compose 檔案和 1 個 JSON 設定檔。需要審慎規劃的是 image tags、磁碟配置、記憶體上限,以及前方的 proxy。

AFFiNE 將文件編輯器與無限畫布放在同一個工作區中,因此同一個頁面可以作為文件閱讀,也可以展開成白板。如果您仍在決定要執行哪個系統,請先閱讀 自架 Notion 替代方案的比較。本指南假設您已經做出選擇,重點是正確執行 AFFiNE,而不是再次進行比較。

本指南的內容已於 8 August 2026 依據 AFFiNE 自架文件與已發布的 release files 完成確認。當日最新的 stable release 為 0.27.3,發布日期為 23 July 2026。

四個容器實際負責的工作

affine 將伺服器與 Web 用戶端整合在同一個映像檔中。它監聽 3010 埠。

affine_migration 是一次性工作,會執行 node ./scripts/self-host-predeploy.js、套用資料庫遷移,然後結束。應用程式對該工作宣告 condition: service_completed_successfully,因此遷移以非零狀態結束時,affine 完全不會啟動。Web 介面無法啟動時,應先查看該工作的日誌。

postgres 儲存您的文件、使用者、工作區與權限。發布的映像檔是 pgvector/pgvector:pg16,也就是內建 pgvector 擴充功能的標準 Postgres 16。pgvector 將 vector 欄位類型加入 Postgres。這種數值格式用於儲存 embedding,讓系統能依文字的語意進行搜尋。

redis 是必要相依服務:伺服器與遷移工作都會等待其健康檢查通過後才啟動。請注意,發布的 compose 檔案並未為 Redis 提供 volume。其中的任何內容都不會在 docker compose down 後保留。這清楚表示其中不含您的資料,也不需要備份。

為什麼 Postgres 映像是 pgvector,而不是 stock postgres

這項需求來自 AFFiNE 的 schema,而不是偏好設定。在 schema.prisma 中,datasource 宣告了 extensions = [pgvector(map: "vector")],且有 4 個資料表包含類型為 vector(1024)embedding 欄位。無論是否啟用 AI 功能,migration job 都會建立這些資料表。因此,資料庫必須先存在該 extension,migration 才能完成。若改用 postgres:16,extension 就會消失,migration 無法建立這些欄位,伺服器會一直等待已失敗的 job。

AFFiNE 從 0.21 版開始改用 pgvector 映像。若安裝版本早於此版本,僅修改映像設定行並不足以完成升級。因此,在執行任何 pull 操作前,請先閱讀 AFFiNE self-host 文件 中的升級頁面。

另外請注意該 tag。pg16 代表 Postgres 16,而 Postgres major version 不能直接遞增。若在現有資料目錄上將版本改為 pg17,Postgres 會拒絕啟動,並在 docker compose logs postgres 顯示類似 The data directory was initialized by PostgreSQL version 16, which is not compatible with this version 17 的訊息。major version 升級必須先 dump,再還原到新的資料目錄。

自架 AFFiNE 需要多少 CPU 和 RAM

AFFiNE 的需求頁面要求至少 4 個 CPU 核心與 2 GB RAM;當文件超過 10,000 字時,記憶體需求會提高到 4 GB。同一頁面也說明記憶體的用途:同步系統與文件合併。頁面提供了一個值得記住的數據:合併包含 10,000 項修改的文件時,記憶體使用量可能達到 1 GB 的峰值。

接著考量 2 GB 方案搭配 2 人共同編輯的情況。平均使用量沒有問題。Postgres 與 Node 程序都會低於上限,並保留一些餘裕。問題在於峰值。單次大型合併可能在既有記憶體使用量之外,再要求 1 GB;而在沒有 swap 的 2 GB 主機上,kernel 的 out-of-memory (OOM) killer 會透過終止占用記憶體最多的程序來滿足這項要求,而該程序就是 AFFiNE server。

你的同事不會看到錯誤訊息。他們只會看到頁面重新載入,因為 restart: unless-stopped 會在幾秒內讓容器重新啟動。不要猜測,請直接確認:

docker inspect affine_server --format '{{.State.OOMKilled}} {{.RestartCount}}'
sudo dmesg -T | grep -i -E 'out of memory|killed process'

第一個命令輸出中的 true,或第二個命令輸出中列出 nodeKilled process 行,表示記憶體已耗盡,而不是發現程式錯誤。請從兩端著手修正。先加入 swap,讓突發需求變慢,而不是直接失敗:

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
free -h

free -h 現在應該會回報 2.0Gi 的 swap 總量。swap 不會讓 AFFiNE 變快,也不是為此設計的。它會將 1 秒的突發需求轉為較慢的執行,而不是讓容器停止。修正的另一端,是避免 Postgres 將快取擴張到應用程式在合併時需要的空間;這就是 Compose 服務的記憶體限制 的用途。

儲存空間則容易得多。以下是 AFFiNE 在同一頁面公布的數據:

ChartPublished AFFiNE storage figures, August 2026
The data behind this chart
[
  {
    "label": "Server install",
    "gb": 1.5
  },
  {
    "label": "Postgres per 1,000 docs",
    "gb": 0.1
  },
  {
    "label": "Blob store per 1,000 uploads",
    "gb": 10
  }
]

伺服器安裝需要 1.5 GB。1,000 份各約 1,000 字的文件只會增加 0.1 GB 的 Postgres 資料,幾乎可以忽略。1,000 個上傳檔案則會增加 10 GB,這才是主要因素。這些是公布的規劃數據,不是從執行中的執行個體測得的數值,因此應將其視為容量成長趨勢,而不是保證。重要的是這個趨勢:資料庫會維持小型,而上傳檔案決定磁碟需求。

自行撰寫 compose 檔案,並固定映像標籤

文件中的安裝方式會使用 curl -L -o docker-compose.yml https://github.com/toeverything/AFFiNE/releases/latest/download/docker-compose.yml 下載預先建立的檔案。這種方式可以正常運作。不過,在依賴該檔案前,有一項細節值得先了解:截至 2026 年 8 月 8 日,附加在 release 0.27.3 的檔案仍會從 .env 檔案讀取路徑,使用 ${UPLOAD_LOCATION}${CONFIG_LOCATION}${DB_DATA_LOCATION};文件的參考頁面則顯示較新的配置,所有內容都放在 ./data 下,完全不需要 .env。這兩種配置都是真實存在的。自行撰寫檔案即可解決這項差異,而且你本來就必須編輯檔案,才能固定映像版本並設定資料庫密碼。

mkdir -p ~/affine/config ~/affine/data
cd ~/affine
printf 'DB_PASSWORD=%s\n' "$(openssl rand -hex 24)" > .env
chmod 600 .env

Compose 會自行從專案目錄讀取 .env,並替你代入 ${DB_PASSWORD},因此密碼不會出現在你貼到支援討論串中的檔案裡。這項習慣值得套用到你執行的每個 stack;相關理由請參閱避免將秘密資訊放入 compose 檔案

現在撰寫 ~/affine/docker-compose.yml

name: affine
services:
  affine:
    image: ghcr.io/toeverything/affine:stable
    container_name: affine_server
    ports:
      - '127.0.0.1:3010:3010'
    depends_on:
      redis:
        condition: service_healthy
      postgres:
        condition: service_healthy
      affine_migration:
        condition: service_completed_successfully
    volumes:
      - ./data/storage:/root/.affine/storage
      - ./config:/root/.affine/config
    environment:
      - REDIS_SERVER_HOST=redis
      - DATABASE_URL=postgresql://affine:${DB_PASSWORD}@postgres:5432/affine
      - AFFINE_INDEXER_ENABLED=false
    restart: unless-stopped

  affine_migration:
    image: ghcr.io/toeverything/affine:stable
    container_name: affine_migration_job
    command: ['sh', '-c', 'node ./scripts/self-host-predeploy.js']
    volumes:
      - ./data/storage:/root/.affine/storage
      - ./config:/root/.affine/config
    environment:
      - REDIS_SERVER_HOST=redis
      - DATABASE_URL=postgresql://affine:${DB_PASSWORD}@postgres:5432/affine
      - AFFINE_INDEXER_ENABLED=false
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy

  redis:
    image: redis:8-alpine
    container_name: affine_redis
    healthcheck:
      test: ['CMD', 'redis-cli', '--raw', 'incr', 'ping']
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

  postgres:
    image: pgvector/pgvector:pg16
    container_name: affine_postgres
    volumes:
      - ./data/postgres:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: affine
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: affine
      POSTGRES_INITDB_ARGS: '--data-checksums'
    healthcheck:
      test: ['CMD', 'pg_isready', '-U', 'affine', '-d', 'affine']
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

這份檔案與上游發布的檔案有 4 項差異,每一項都有其原因。

  • 127.0.0.1:3010:3010 僅在 loopback 位址發布連接埠,因此在你決定存取方式前,伺服器外部無法連線到 AFFiNE。上游的 '3010:3010' 會繫結所有介面,而在大多數 VPS 映像中,這也包含公開介面。
  • POSTGRES_HOST_AUTH_METHOD: trust 已移除,改為設定密碼。Trust 驗證會允許任何連線到該資料庫的連線,以 affine 使用者身分且不需要密碼進行驗證。這項設定僅限於私有 Compose network;但如果日後將另一個容器連接到該 network,或為了除錯而發布 5432,就會造成問題。
  • redis:8-alpine 取代了單獨使用的 redis;該標籤會解析為 latest。截至 2026 年 8 月,這是 Redis 8,因此固定版本可保留已測試的 major version,避免未來 Redis 9 在無關的 docker compose pull 期間到達。
  • pgvector/pgvector:pg16 仍完全依照上游的設定,原因如上所述。

POSTGRES_PASSWORD 只會在 Postgres 首次建立資料目錄時讀取。在已存在的 instance 上,請使用 docker compose exec postgres psql -U affine -c "ALTER USER affine WITH PASSWORD 'yourpassword'" 設定密碼,然後更新 DATABASE_URL 使其相符。

設定位於 config/config.json

AFFiNE 會從 config/config.json 讀取設定。這是您掛載到 /root/.affine/config 的目錄。系統不會替您建立該檔案,因此請在第一次啟動前建立。使用編輯器開啟 ~/affine/config/config.json,並填入以下內容,將範例中的網域替換成您自己的網域:

{
  "$schema": "https://github.com/toeverything/affine/releases/latest/download/config.schema.json",
  "server": {
    "name": "Team workspace",
    "externalUrl": "https://affine.example.com"
  },
  "copilot": {
    "enabled": false,
    "byok": {
      "enabled": false
    }
  }
}

server.externalUrl 必須是使用者實際在瀏覽器中開啟的位址。AFFiNE 會根據這個值建立分享連結與工作區邀請。如果保留為 http://localhost:3010,您傳送的邀請會指向收件者自己的電腦,並在對方的電腦上失敗。請在第一次啟動前將其設為公開 HTTPS 位址,確保檔案與管理面板使用相同的位址。

copilot 控制 AI 功能。copilot.byok.enabled 是自帶金鑰開關,允許工作區擁有者在工作區設定中貼上自己的模型供應商金鑰。自行託管 AFFiNE 不包含 AI 訂閱。如果不需要 AI 功能,請將兩者都保留為 false

啟動整個堆疊:

docker compose up -d
docker compose ps

docker compose ps 應列出 affine_postgresaffine_redis,且狀態為 healthy;affine_server 應為 running;affine_migration_job 的狀態應為 exited (0)。移轉工作出現任何其他結束代碼時,都應進一步追查。其日誌會指出停止的步驟:

docker compose logs affine_migration

固定映像版本,避免日後遺忘

stable 是會變動的 tag。AFFiNE 的發布流程會為每個穩定版本指向數個 tag,其中有兩個與此處相關:stable 會在每次發布時重新指向新版本,stable- 則會接在 git short hash 後方,且不會變動。若持續使用 stable,六個月後執行 docker compose pull 會取得不同的映像,並在非預期的時間對資料庫執行 migration。請固定使用已測試的確切映像:

docker compose pull
docker image inspect ghcr.io/toeverything/affine:stable --format '{{index .RepoDigests 0}}'

此命令會輸出類似 ghcr.io/toeverything/affine@sha256: 加上一長串 hash 的內容。將完整字串貼到 affineaffine_migrationimage: 行。這兩處必須始終一致,因為它們是同一個映像所扮演的兩個角色;若不一致,可能會將資料庫 migration 至一種 schema,卻使用另一種 schema 提供服務。如此一來,升級會變成有意執行的編輯,而不是突發變更:修改 digest、備份、docker compose pulldocker compose up -d

先建立管理員帳號,避免被他人搶先建立

在全新的執行個體上開啟 /admin 時,AFFiNE 會將你導向帳號建立頁面,因為伺服器尚未設定管理員。這個流程不需要邀請碼,也沒有設定 token。第一個載入該頁面的人會成為伺服器的管理員,因此完成註冊前,必須保持連接埠關閉。

這就是上方 compose 檔案繫結至 127.0.0.1 的原因。請從自己的電腦建立 SSH tunnel 來連線:

ssh -L 3010:127.0.0.1:3010 you@your-server-ip

讓該 tunnel 持續執行,並在本機瀏覽器中開啟 http://127.0.0.1:3010/admin。完成註冊並登入後,關閉 tunnel。到這時,才可以安全地讓此執行個體使用公開網域名稱。

AFFiNE 儲存資料的位置

這 3 個路徑包含所有資料,而且都位於您建立的目錄中。

  • ./data/postgres 是 Postgres 資料目錄,包含文件、使用者、工作區與權限。
  • ./data/storage 會掛載至容器中的 /root/.affine/storage,包含所有上傳的檔案。
  • ./config 會掛載至 /root/.affine/config,包含 config.json

上游在此使用 bind mount,而不是 named volume。這是刻意的設計:您可以使用一般命令對這些路徑執行 tar 與複製,不必要求 Docker 找出資料的存放位置。代價是,主機上的檔案擁有權現在由您管理;這項取捨會在 bind mount 與 named volume 中說明。

如何備份 AFFiNE

需要備份兩項內容,而且備份方式不同。資料庫是執行中的伺服器,因此在服務執行時直接複製其檔案會產生損毀的副本。請改用 dump:

mkdir -p ~/affine/backup
cd ~/affine
docker compose exec -T postgres pg_dump --format c --username affine affine \
  > backup/affine-$(date +%F).dump
ls -lh backup/

dump 會在容器內透過本機 socket 執行,因此不會要求輸入密碼。請檢查 ls 輸出的檔案大小。只有數百 bytes 的檔案表示 dump 失敗,但 shell 仍建立了檔案;這通常會在 6 個月後才被發現。-T 也很重要:若缺少它,Compose 可能會配置 terminal,進而損毀 binary stream。

上傳的檔案就是一般檔案,因此使用 tar:

tar czf backup/storage-$(date +%F).tgz -C data storage
cp config/config.json backup/config-$(date +%F).json

請手動將 config.json 納入備份。AFFiNE 文件截至 August 2026 仍將管理面板的設定匯出列為尚未實作,因此磁碟上的檔案是設定的唯一副本。請將這 3 個檔案全部複製到伺服器之外。備份若與受保護的資料位於同一個磁碟上,就不算備份。

還原,以及官方步驟中的一個陷阱

在需要還原之前,先閱讀官方還原步驟,並仔細核對內容。截至 2026 年 8 月,官方步驟會將名為 affine.backup 的檔案複製到容器中,然後從 ./pg.backup 還原;這是兩個不同的名稱。此外,步驟會移除 ./postgres 目錄,但目前的 compose file 會將資料保存在 ./data/postgres。請依照實際使用的路徑操作,不要直接套用片段中的路徑。以下是本指南所用配置的操作順序:

cd ~/affine
docker compose down
sudo mv data/postgres data/postgres.old
docker compose up -d postgres
docker compose cp backup/affine-2026-08-08.dump postgres:/tmp/affine.dump
docker compose exec postgres pg_restore --format c --username affine \
  --dbname affine --verbose /tmp/affine.dump
docker compose up -d

請注意,這裡使用的是 mv,而不是 rm。如果沒有保留資料庫副本就直接覆寫資料庫,一個錯誤指令可能造成全部資料遺失;先將舊目錄移至其他位置則不會有額外成本。也要使用 tar xzf backup/storage-2026-08-08.tgz -C data 還原 uploads,否則所有文件的附件都會無法正常顯示。接著登入,開啟一份包含影像的文件。這就是測試方式。未在瀏覽器中開啟確認過的還原結果,只是檔案,不是備份。

將 AFFiNE 放在既有的 proxy 後方

AFFiNE 使用 WebSocket,而且這不是可選功能。官方文件明確指出:WebSocket 是 AFFiNE 同步與協作系統的基礎。因此,未升級這些連線的 proxy 會讓工作區停止同步,且不會明顯顯示錯誤。頁面可以載入,登入也能成功,但在一個瀏覽器中所做的修改不會出現在另一個瀏覽器中。請在瀏覽器的開發人員工具中開啟 Network 分頁,並篩選 WS。若某個連線反覆開啟又關閉,表示 proxy 未傳遞 upgrade。

如果你已經使用 Traefik 處理其他容器,AFFiNE 可作為一般服務加入其中。從 affine service 移除 ports: 區塊,然後加入:

    networks:
      - default
      - proxy
    labels:
      - 'traefik.enable=true'
      - 'traefik.docker.network=proxy'
      - 'traefik.http.routers.affine.rule=Host(`affine.example.com`)'
      - 'traefik.http.routers.affine.entrypoints=websecure'
      - 'traefik.http.routers.affine.tls.certresolver=letsencrypt'
      - 'traefik.http.services.affine.loadbalancer.server.port=3010'

在檔案底部、與 services: 同一層級的位置加入:

networks:
  proxy:
    external: true

certificate resolver 名稱必須與 Traefik 設定中定義的名稱一致,而 loadbalancer.server.port 是容器埠 3010,不是 host port。Traefik 不需額外設定即可 proxy WebSocket 連線,因此無須再加入其他內容。如果你的其他服務已經透過 Authentik 單一登入,可以在此 router 上加入 forward auth middleware,以限制瀏覽器對 AFFiNE 的存取;但請先不要啟用,直到你測試過 desktop app。desktop app 沒有瀏覽器 session,啟用後只會同步失敗。若要讓多個 app 由同一個 Traefik instance 處理,請參閱在多個 app 前方使用單一 Traefik

使用 nginx 時,必須明確要求 upgrade:

location / {
    proxy_pass http://127.0.0.1:3010;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    client_max_body_size 100m;
}

nginx 中的 client_max_body_size 預設為 1 MB。因此,若沒有這一行,所有大於小型相片的上傳都會失敗並回傳 413 status,而 AFFiNE log 中不會出現任何內容,因為 request 根本沒有抵達。Caddy 只需加入一行 reverse_proxy http://127.0.0.1:3010,即可自行處理憑證與 WebSocket upgrade。

自架版本未包含的功能

在將團隊移轉過來之前,請先如實評估這些限制。

系統支援即時協作,而所有資源規模建議都與此功能有關,因為 AFFiNE 的官方文件指出,記憶體使用量來自同步系統與文件合併。離線編輯是許多人選擇 local-first 工具的原因,桌面應用程式也能將自架伺服器加入工作區清單,並使用該伺服器登入。在正式採用前,請先測試團隊實際依賴的離線行為:關閉網路後,在桌面應用程式中編輯;重新連線後,再使用第二台裝置檢查結果。功能清單不是證據,這份清單也一樣。

隨附的 compose 檔案未啟用伺服器端全文搜尋,其中 AFFINE_INDEXER_ENABLED=false 會在伺服器與遷移工作中設定。啟用後必須加入 Manticore Search 容器,服務數量會增加到 5 個,記憶體需求也會提高。在 2 GB 的主機上,這項變更可能讓系統超出容量限制。用戶端內的搜尋功能仍可搜尋目前開啟的工作區。

邀請其他人加入前,有兩項限制值得注意。自架工作區最多可授予 10 個席位,超過此數量就需要向 AFFiNE 購買 Team license。文件將自架執行個體的 unlimited blob storage 與 unlimited blob size 描述為預定支援,但截至 August 2026 仍未完全實作。對家庭或小型團隊而言,這兩項限制都不重要。但如果你原本計畫移轉 40 人,兩項都需要納入考量。

升級

請先閱讀版本資訊,尤其是 0.26 升級至 0.27 這類小版本升級,因為其中可能包含破壞性變更。執行任何操作前,請先備份資料庫與儲存目錄,因為 migration job 會在下次啟動時修改 schema,且無法復原。接著修改固定的 digest,執行 docker compose pull,再執行 docker compose up -d,並監看 docker compose logs -f affine_migration,直到它正常結束。之後,docker image prune 會清除舊的 layers。補充一項與歷史版本相關的資訊:若使用的是非常舊的安裝版本,從 0.23.0 起 image 名稱已由 affine-graphql 變更為 affine。因此,早於該版本的 compose file 必須先改寫其中的 image 設定,執行 pull 時才能找到映像檔。

FAQ

AFFiNE 容器為什麼始終無法啟動?

affine 服務在 affine_migration 工作中宣告了 condition: service_completed_successfully,因此只要 migration 以非 0 狀態結束,伺服器就不會啟動,Web 介面也完全不會出現。執行 docker compose logs affine_migration,查看哪個步驟停止。手動編輯 compose 檔案時,最常見的原因是使用標準的 postgres image 取代 pgvector/pgvector:pg16。這是因為 AFFiNE schema 宣告了 pgvector extension,並使用 vector(1024) 欄位建立資料表,而純 Postgres 無法建立這些欄位。

自架 AFFiNE 需要多少 RAM?

AFFiNE 的需求頁面要求至少 4 個 CPU cores 和 2 GB RAM。文件超過 10,000 個字時,需求會增加到 4 GB。頁面也指出,合併包含 10,000 項修改的文件時,記憶體使用量可能達到 1 GB。對 2 GB 伺服器而言,造成服務終止的是這個尖峰,而不是閒置負載:kernel 的 out-of-memory killer 會停止 AFFiNE process,接著由 restart: unless-stopped 重新啟動,因此使用者看到的是頁面重新載入,而不是錯誤訊息。使用 docker inspect affine_server --format '{{.State.OOMKilled}}'sudo dmesg -T | grep -i 'out of memory' 確認問題,然後新增 2 GB swap file,讓尖峰時的效能變慢,而不是直接導致服務終止。

AFFiNE 將資料儲存在哪裡?哪些資料需要備份?

compose directory 下的 3 個路徑包含所有資料:./data/postgres 用於資料庫、./data/storage 用於上傳檔案,以及 ./config 用於 config.json。使用 docker compose exec -T postgres pg_dump --format c --username affine affine > affine.dump 備份資料庫,不要直接複製檔案,因為執行中的 Postgres 無法安全複製。使用 tar 封存 ./data/storage 中的上傳檔案,並手動保留 config.json 的副本,因為截至 August 2026,管理面板的設定匯出功能仍列為尚未實作。

自架 AFFiNE 支援即時協作嗎?

支援,而且不需要啟用任何功能。唯一的要求是正確設定 reverse proxy,因為同步功能使用 WebSocket connections。在 nginx 中,這表示需要 proxy_http_version 1.1,以及 UpgradeConnection: upgrade headers;Traefik 和 Caddy 則會直接轉送這些 connections,不需要額外設定。如果 proxy 未將 connections upgrade,工作區仍會正常載入並完成登入,但在一個瀏覽器中進行的編輯不會出現在另一個瀏覽器中。

可以搭配標準 Postgres image 執行 AFFiNE 嗎?

不行。AFFiNE 的 schema.prisma 宣告 extensions = [pgvector(map: "vector")],並定義 4 個資料表;每個資料表都有一個類型為 vector(1024)embedding 欄位。即使 AI 功能已關閉,migration job 仍會建立這些資料表。請使用 pgvector/pgvector:pg16,這是內建該 extension 的 Postgres 16。如果改為讓 AFFiNE 連線至外部 Postgres server,請在該 server 上安裝 pgvector,並在目標資料庫中建立 extension,再執行 migration。