Cara Self-host AFFiNE dengan Docker Compose
Ketahui cara menjalankan AFFiNE pada VPS menggunakan Docker Compose. Panduan ini merangkumi empat kontena, konfigurasi tag imej, pengurusan data, serta keperluan RAM 2 GB.
Apa yang anda peroleh apabila melakukan self-host AFFiNE
Self-hosting AFFiNE memberikan anda ruang kerja gaya Notion pada pelayan yang anda kawal, yang berjalan sebagai empat kontena: aplikasi, tugasan migrasi sekali jalan, Postgres, dan Redis. Kolaborasi masa nyata disertakan, sehingga 10 tempat duduk yang diperoleh oleh ruang kerja self-hosted secara lalai. Pemasangannya terdiri daripada satu fail compose dan satu fail konfigurasi JSON. Perkara yang perlu dipertimbangkan ialah tag imej, susun atur cakera, had memori, dan proksi yang anda letakkan di hadapan.
AFFiNE mengekalkan editor dokumen dan kanvas infiniti dalam ruang kerja yang sama, jadi satu halaman boleh dibaca sebagai dokumen atau dibentangkan sebagai papan putih. Jika anda masih membuat keputusan tentang apa yang ingin dijalankan, baca perbandingan alternatif Notion yang di-self-host terlebih dahulu. Panduan ini mengandaikan pilihan telah dibuat, dan merangkumi cara menjalankan AFFiNE dengan betul dan bukannya membandingkannya semula.
Segala maklumat di sini telah disemak berdasarkan dokumentasi self-host AFFiNE dan fail keluaran yang diterbitkan pada 8 Ogos 2026. Keluaran stabil terbaharu pada tarikh tersebut ialah 0.27.3, yang diterbitkan pada 23 Julai 2026.
Fungsi sebenar empat bekas tersebut
affine ialah pelayan dan klien web dalam satu imej. Ia mendengar pada port 3010.
affine_migration ialah tugasan sekali jalan yang menjalankan node ./scripts/self-host-predeploy.js, melaksanakan migrasi pangkalan data, kemudian berhenti. Aplikasi mengisytiharkan condition: service_completed_successfully pada tugasan tersebut, jadi migrasi yang berhenti dengan status bukan sifar bermakna affine tidak akan bermula sama sekali. Apabila antara muka web tidak muncul, log tugasan tersebut adalah perkara pertama yang perlu dibaca.
postgres menyimpan dokumen, pengguna, ruang kerja dan kebenaran anda. Imej yang dibekalkan ialah pgvector/pgvector:pg16, iaitu Postgres 16 biasa dengan sambungan pgvector yang telah dikompilasi. pgvector menambah jenis lajur vector kepada Postgres, iaitu bentuk numerik yang digunakan untuk menyimpan embedding supaya teks boleh dicari berdasarkan makna.
redis ialah dependency wajib: kedua-dua pelayan dan tugasan migrasi menunggu pemeriksaan kesihatannya sebelum bermula. Perhatikan apa yang tidak diberikan oleh fail compose yang dibekalkan kepada Redis, iaitu volume. Tiada apa-apa di dalamnya yang bertahan selepas docker compose down, dan ini memberitahu anda dengan jelas bahawa ia tidak menyimpan kandungan anda dan tidak memerlukan sandaran.
Mengapa imej Postgres menggunakan pgvector dan bukan postgres biasa
Keperluan ini datang daripada skema AFFiNE, bukan kerana pilihan peribadi. Dalam schema.prisma, sumber data mengisytiharkan extensions = [pgvector(map: "vector")], dan empat jadual mengandungi lajur embedding yang ditaip sebagai vector(1024). Tugasan migrasi akan mencipta jadual tersebut sama ada anda menghidupkan ciri AI atau tidak, jadi sambungan (extension) tersebut mesti wujud dalam pangkalan data sebelum migrasi boleh selesai. Jika anda menukar kepada postgres:16, sambungan tersebut tiada, migrasi tidak dapat mencipta lajur tersebut, dan pelayan akan terus menunggu tugasan yang telah gagal.
AFFiNE beralih kepada imej pgvector pada versi 0.21. Bagi pemasangan yang lebih lama daripada itu, menyunting baris imej bukanlah langkah naik taraf yang lengkap, jadi baca halaman naik taraf dalam dokumentasi self-host AFFiNE sebelum anda menarik (pull) sebarang imej.
Satu lagi perkara mengenai tag tersebut. pg16 bermaksud Postgres 16, dan versi utama Postgres bukanlah nombor yang boleh anda tingkatkan sewenang-wenangnya. Jika anda menukarnya kepada pg17 pada direktori data sedia ada, Postgres akan enggan bermula, dengan baris seperti The data directory was initialized by PostgreSQL version 16, which is not compatible with this version 17 dalam docker compose logs postgres. Peralihan versi utama bermaksud anda perlu melakukan dump dan restore ke dalam direktori data yang baharu.
Berapakah keperluan CPU dan RAM untuk AFFiNE yang dihoskan sendiri
Halaman keperluan AFFiNE menetapkan sekurang-kurangnya 4 teras CPU dan 2 GB RAM, dan meningkat kepada 4 GB apabila dokumen anda melebihi 10,000 perkataan. Halaman yang sama menjelaskan ke mana memori tersebut digunakan: sistem penyelarasan (sync) dan penggabungan dokumen. Terdapat satu angka yang perlu diingati, iaitu penggabungan dokumen dengan 10,000 pengubahsuaian boleh mencapai puncak penggunaan sebanyak 1 GB.
Sekarang, bandingkan angka tersebut dengan pelan 2 GB yang digunakan oleh dua orang penulis. Secara purata, ia mencukupi. Postgres dan proses Node berada di bawah had tersebut dengan ruang yang masih berbaki. Masalahnya ialah pada waktu puncak. Satu penggabungan besar boleh meminta 1 GB tambahan di atas penggunaan sedia ada, dan pada pelayan 2 GB tanpa swap, OOM (out-of-memory) killer kernel akan bertindak balas terhadap permintaan tersebut dengan mematikan proses terbesar, iaitu pelayan AFFiNE.
Rakan sekerja anda tidak akan melihat ralat. Mereka hanya melihat halaman dimuat semula, kerana restart: unless-stopped akan menghidupkan semula kontena dalam beberapa saat. Jangan meneka, sahkan perkara ini:
docker inspect affine_server --format '{{.State.OOMKilled}} {{.RestartCount}}'
sudo dmesg -T | grep -i -E 'out of memory|killed process'true daripada arahan pertama, atau baris Killed process yang menamakan node daripada arahan kedua, bermakna anda kehabisan memori dan bukannya menemui pepijat. Selesaikan masalah ini dari kedua-dua arah. Tambahkan swap terlebih dahulu, supaya lonjakan penggunaan menjadi perlahan dan bukannya membawa maut:
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 -hfree -h kini sepatutnya melaporkan jumlah swap sebanyak 2.0Gi. Swap tidak menjadikan AFFiNE pantas, dan ia bukan bertujuan untuk itu. Ia menukarkan lonjakan satu saat menjadi kelewatan sesaat dan bukannya kontena yang mati. Langkah penyelesaian seterusnya ialah menghalang Postgres daripada mengembangkan cache ke dalam ruang yang diperlukan oleh aplikasi semasa proses penggabungan, iaitu fungsi had memori pada servis Compose.
Storan adalah jauh lebih mudah untuk diramal. Berikut adalah angka yang diterbitkan oleh AFFiNE pada halaman yang sama:
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
}
]Pemasangan pelayan mengambil 1.5 GB. Seribu dokumen dengan kira-kira seribu perkataan setiap satu menambah 0.1 GB data Postgres, yang hampir tidak memberi kesan. Seribu fail yang dimuat naik menambah 10 GB, dan itulah keseluruhan ceritanya. Ini adalah angka perancangan yang diterbitkan dan bukannya ukuran daripada instans yang sedang berjalan, jadi anggaplah ia sebagai anggaran kasar dan bukannya janji. Bentuknya adalah perkara yang penting: pangkalan data anda kekal kecil, dan fail yang dimuat naik menentukan penggunaan cakera anda.
Tulis fail compose anda sendiri, dengan tag yang dipinkan
Pemasangan yang didokumentasikan memuat turun fail siap guna dengan curl -L -o docker-compose.yml https://github.com/toeverything/AFFiNE/releases/latest/download/docker-compose.yml. Cara itu berkesan. Satu perincian perlu diketahui sebelum anda bergantung kepadanya: setakat 8 Ogos 2026, fail yang dilampirkan pada keluaran 0.27.3 masih membaca laluan daripada fail .env, menggunakan ${UPLOAD_LOCATION}, ${CONFIG_LOCATION} dan ${DB_DATA_LOCATION}, manakala halaman rujukan dokumentasi menunjukkan susun atur lebih baharu yang menyimpan segala-galanya di bawah ./data dan tidak memerlukan .env langsung. Kedua-duanya adalah tulen. Menulis fail sendiri menyelesaikan persoalan tersebut, dan anda tetap perlu menyuntingnya untuk meminkan imej serta menetapkan kata laluan pangkalan data.
mkdir -p ~/affine/config ~/affine/data
cd ~/affine
printf 'DB_PASSWORD=%s\n' "$(openssl rand -hex 24)" > .env
chmod 600 .envCompose membaca .env daripada direktori projek secara automatik dan menggantikan ${DB_PASSWORD} untuk anda, jadi kata laluan tidak akan muncul dalam fail yang anda tampal ke dalam thread sokongan. Tabiat itu wajar dikekalkan merentas setiap stack yang anda jalankan, dan alasannya ada dalam menyimpan rahsia di luar fail compose.
Sekarang, tulis ~/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-stoppedTerdapat empat perbezaan daripada fail yang dibekalkan oleh pihak hulu (upstream), dan setiap satunya mempunyai sebab.
127.0.0.1:3010:3010menerbitkan port pada alamat loopback sahaja, jadi tiada apa-apa di luar pelayan boleh mencapai AFFiNE sehingga anda memutuskan caranya.'3010:3010'pihak hulu mengikat setiap antara muka, dan pada kebanyakan imej VPS, ini termasuk antara muka awam.POSTGRES_HOST_AUTH_METHOD: trusttelah dibuang dan kata laluan ditetapkan sebagai ganti. Pengesahan trust menerima sebarang sambungan ke pangkalan data tersebut sebagai penggunaaffinetanpa kata laluan. Ia terhad kepada rangkaian Compose peribadi, yang memadai sehingga hari anda menyambungkan satu lagi kontena ke rangkaian tersebut atau menerbitkan 5432 semasa proses nyahpepijat.redis:8-alpinemenggantikanrediskosong, yang diselesaikan kepadalatest. Setakat Ogos 2026, itu adalah Redis 8, jadi pin tersebut mengekalkan versi utama yang anda uji dan menghalang Redis 9 daripada tiba secara tidak sengaja semasadocker compose pullyang tidak berkaitan.pgvector/pgvector:pg16kekal tepat seperti yang ditetapkan oleh pihak hulu, atas alasan yang diberikan di atas.
POSTGRES_PASSWORD hanya dibaca apabila Postgres mencipta direktori datanya buat kali pertama. Pada instans yang sudah wujud, tetapkan kata laluan dengan docker compose exec postgres psql -U affine -c "ALTER USER affine WITH PASSWORD 'yourpassword'" dan kemudian kemas kini DATABASE_URL agar sepadan.
Konfigurasi disimpan dalam config/config.json
AFFiNE membaca tetapan daripada config/config.json, iaitu direktori yang anda lekapkan (mount) pada /root/.affine/config. Tiada proses yang akan mencipta fail tersebut untuk anda, jadi tulis fail itu sebelum permulaan pertama. Buka ~/affine/config/config.json dalam editor dan masukkan kandungan ini, dengan menggantikan contoh domain dengan domain anda sendiri:
{
"$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 mestilah alamat yang sebenarnya dibuka oleh pengguna anda dalam pelayar web. AFFiNE membina pautan perkongsian dan jemputan ruang kerja (workspace) berdasarkan nilai tersebut. Jika ia dibiarkan pada http://localhost:3010, jemputan yang anda hantar akan menghalakan penerima ke mesin mereka sendiri dan gagal di situ. Tetapkan kepada alamat HTTPS awam sebelum permulaan pertama, supaya fail tersebut dan panel pentadbir sentiasa selari.
copilot mengawal ciri AI. copilot.byok.enabled ialah suis untuk membawa kunci anda sendiri (bring-your-own-key), yang membolehkan pemilik ruang kerja menampal kunci penyedia model mereka sendiri ke dalam tetapan ruang kerja. Pengehosan kendiri (self-hosting) AFFiNE tidak menyertakan langganan AI. Biarkan kedua-duanya false jika anda tidak mahukannya.
Mulakan stack:
docker compose up -d
docker compose psdocker compose ps sepatutnya menyenaraikan affine_postgres dan affine_redis sebagai sihat (healthy), affine_server sebagai sedang berjalan (running), dan affine_migration_job dengan status exited (0). Sebarang kod keluar (exit code) lain pada tugasan migrasi adalah perkara yang perlu disiasat, dan lognya akan menamakan langkah yang terhenti:
docker compose logs affine_migrationSematkan imej sebelum anda terlupa
stable ialah tag yang berubah-ubah. Aliran kerja keluaran AFFiNE menghalakan beberapa tag pada setiap binaan stabil, dan dua daripadanya penting di sini: stable, yang dihalakan semula pada setiap keluaran, dan stable- yang diikuti oleh hash pendek git, yang tidak berubah. Jika dibiarkan pada stable, docker compose pull enam bulan dari sekarang akan mengambil imej yang berbeza dan menjalankan migrasi terhadap pangkalan data anda pada waktu yang tidak anda tetapkan. Sematkan imej tepat yang telah anda uji:
docker compose pull
docker image inspect ghcr.io/toeverything/affine:stable --format '{{index .RepoDigests 0}}'Perintah tersebut akan memaparkan baris seperti ghcr.io/toeverything/affine@sha256: diikuti oleh hash yang panjang. Tampal keseluruhan rentetan tersebut ke dalam baris image: bagi kedua-dua affine dan affine_migration. Kedua-duanya mesti sentiasa sepadan, kerana ia adalah imej yang sama yang memainkan dua peranan, dan ketidakpadanan bermakna melakukan migrasi pangkalan data kepada satu skema sementara melayaninya dengan skema yang lain. Menaik taraf kemudiannya menjadi satu suntingan yang disengajakan dan bukannya kejutan: tukar digest, buat sandaran, docker compose pull, docker compose up -d.
Cipta akaun pentadbir sebelum orang lain melakukannya
Buka /admin pada instans baharu dan AFFiNE akan menghalakan anda ke halaman penciptaan akaun, kerana pelayan tersebut belum mempunyai pentadbir. Tiada kod jemputan atau token persediaan dalam aliran tersebut. Orang pertama yang memuatkan halaman itu akan menjadi pentadbir pelayan anda, jadi port tersebut mesti kekal tertutup sehingga anda selesai mendaftar.
Itulah sebabnya fail compose di atas mengikat kepada 127.0.0.1. Capai ia melalui terowong SSH dari mesin anda sendiri:
ssh -L 3010:127.0.0.1:3010 you@your-server-ipBiarkan ia berjalan dan buka http://127.0.0.1:3010/admin dalam pelayar tempatan anda. Daftar dan log masuk, kemudian tutup terowong tersebut. Hanya selepas itu barulah selamat untuk meletakkan instans tersebut pada nama domain awam.
Lokasi data AFFiNE disimpan
Tiga laluan menyimpan segala-galanya, dan kesemuanya terletak di dalam direktori yang anda cipta.
./data/postgresialah direktori data Postgres: dokumen, pengguna, ruang kerja, dan kebenaran../data/storagedipasang pada/root/.affine/storagedi dalam kontena dan menyimpan setiap fail yang dimuat naik../configdipasang pada/root/.affine/configdan menyimpanconfig.json.
Upstream menggunakan bind mount di sini dan bukannya named volume, dan pilihan itu dibuat dengan sengaja: anda boleh menggunakan tar dan menyalin laluan ini dengan arahan biasa, tanpa perlu menyemak lokasi Docker menyimpannya. Kosnya ialah pemilikan fail pada hos kini menjadi tanggungjawab anda, iaitu pertukaran yang dibincangkan dalam bind mounts dan named volumes.
Cara membuat sandaran AFFiNE
Terdapat dua perkara yang perlu disandarkan, dan kaedah sandarannya adalah berbeza. Pangkalan data merupakan pelayan yang sedang berjalan, jadi menyalin failnya semasa ia beroperasi akan menghasilkan salinan yang rosak. Sebaliknya, lakukan 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/Proses dump berjalan di dalam container melalui soket tempatannya, jadi ia tidak akan meminta kata laluan. Semak saiz fail dalam output ls tersebut. Fail yang bersaiz beberapa ratus bait bermakna proses dump gagal walaupun shell tetap mencipta fail tersebut; ini adalah kegagalan yang biasanya disedari pengguna enam bulan kemudian. -T juga penting: tanpanya, Compose boleh memperuntukkan terminal dan merosakkan aliran binari.
Fail yang dimuat naik hanyalah fail biasa, jadi gunakan tar untuk menyandarkannya:
tar czf backup/storage-$(date +%F).tgz -C data storage
cp config/config.json backup/config-$(date +%F).jsonSimpan config.json dalam sandaran anda secara manual. Dokumentasi AFFiNE masih menyenaraikan eksport konfigurasi daripada panel admin sebagai belum dilaksanakan, disemak pada Ogos 2026, jadi fail pada cakera adalah satu-satunya salinan tetapan anda. Salin ketiga-tiga fail tersebut keluar dari pelayan. Sandaran yang disimpan pada cakera yang sama dengan data asal bukanlah sandaran yang sebenar.
Pemulihan, dan satu perangkap dalam langkah yang diterbitkan
Baca langkah pemulihan rasmi sebelum anda memerlukannya, dan baca dengan teliti. Seperti yang diterbitkan pada Ogos 2026, mereka menyalin fail bernama affine.backup ke dalam kontena dan kemudian memulihkan daripada ./pg.backup, iaitu dua nama yang berbeza, dan mereka membuang direktori ./postgres sedangkan fail compose semasa menyimpan datanya dalam ./data/postgres. Ikut laluan yang anda gunakan sebenarnya dan bukannya laluan dalam coretan tersebut. Berikut adalah urutan berdasarkan susun atur dalam panduan ini:
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 -dPerhatikan mv dan bukannya rm. Melakukan pemulihan ke atas pangkalan data yang anda tidak simpan salinannya adalah punca satu arahan yang salah menjadi kehilangan data sepenuhnya, dan mengalihkan direktori lama tidak menelan sebarang kos. Pulihkan juga muat naik dengan tar xzf backup/storage-2026-08-08.tgz -C data, atau setiap dokumen akan dipaparkan dengan lampiran yang rosak. Kemudian log masuk dan buka dokumen yang mengandungi imej. Itu adalah ujiannya. Pemulihan yang belum anda buka dalam pelayar hanyalah satu fail, bukan sandaran.
Meletakkan AFFiNE di belakang proksi sedia ada
AFFiNE menggunakan WebSocket, dan ia adalah wajib. Dokumentasinya menyatakan dengan jelas: WebSocket ialah asas bagi sistem penyelarasan dan kolaborasi AFFiNE. Oleh itu, proksi yang tidak menaik taraf sambungan tersebut akan menyebabkan ruang kerja anda berhenti menyelaraskan suntingan secara senyap. Halaman dimuatkan, log masuk berfungsi, namun suntingan yang dibuat dalam satu pelayar tidak akan sampai ke pelayar yang lain. Dalam alat pembangun pelayar anda, buka tab Network dan tapis kepada WS. Sambungan yang dibuka dan ditutup berulang kali menandakan proksi tersebut tidak melepasi naik taraf (upgrade) sambungan.
Jika anda sudah menjalankan Traefik untuk kontena lain, AFFiNE boleh ditambah sebagai servis biasa. Padamkan blok ports: daripada servis affine, kemudian tambah:
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'dan di bahagian bawah fail, bersebelahan dengan services::
networks:
proxy:
external: trueNama penyelesai sijil (certificate resolver) mestilah sepadan dengan yang ditakrifkan dalam konfigurasi Traefik anda, dan loadbalancer.server.port ialah port kontena 3010, bukan port hos. Traefik memproksi sambungan WebSocket tanpa memerlukan konfigurasi tambahan, jadi tiada apa lagi yang perlu ditambah. Jika keseluruhan tindanan (stack) anda sudah berada di belakang Authentik untuk single sign-on, middleware pengesahan hadapan (forward auth) pada router ini akan mengawal akses pelayar ke AFFiNE. Walau bagaimanapun, jangan aktifkannya sehingga anda selesai menguji aplikasi desktop, kerana aplikasi tersebut tidak membawa sesi pelayar dan akan gagal menyelaraskan data. Menjalankan beberapa aplikasi di belakang satu instans proksi diterangkan dalam satu Traefik di hadapan beberapa aplikasi.
Pada nginx, anda perlu meminta naik taraf secara eksplisit:
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;
}client_max_body_size ditetapkan kepada 1 MB secara lalai dalam nginx. Tanpa baris tersebut, setiap muat naik yang lebih besar daripada foto kecil akan gagal dengan status 413 dan tiada apa-apa yang muncul dalam log AFFiNE, kerana permintaan tersebut tidak pernah sampai. Caddy hanya memerlukan satu baris, reverse_proxy http://127.0.0.1:3010, dan ia mengendalikan sijil serta naik taraf WebSocket secara automatik.
Perkara yang tiada dalam binaan self-hosted
Bersikap jujurlah dengan diri sendiri mengenai perkara ini sebelum anda memindahkan pasukan anda.
Kolaborasi masa nyata tersedia, dan ia merupakan ciri yang menjadi fokus bagi semua nasihat saiz pelayan, memandangkan dokumentasi AFFiNE sendiri mengaitkan penggunaan memori dengan sistem penyelarasan (sync) dan penggabungan dokumen. Penyuntingan luar talian (offline) adalah sebab utama ramai orang mahukan alat yang mengutamakan storan tempatan (local-first), dan aplikasi desktop boleh menambah pelayan self-hosted anda ke dalam senarai ruang kerja serta log masuk ke dalamnya. Uji gelagat luar talian sebenar yang diperlukan oleh pasukan anda sebelum anda membuat keputusan: buat suntingan dalam aplikasi desktop semasa rangkaian dimatikan, sambungkan semula, kemudian semak hasilnya pada peranti kedua. Senarai ciri bukanlah bukti, dan itu termasuk senarai ini.
Carian teks penuh (full-text search) di sebelah pelayan dimatikan dalam fail compose yang dibekalkan, di mana AFFINE_INDEXER_ENABLED=false ditetapkan pada pelayan dan pada tugasan migrasi. Menghidupkannya bermakna menambah kontena Manticore Search, yang merupakan servis kelima dan memerlukan lebih banyak memori. Pada mesin 2 GB, perubahan inilah yang akan menyebabkan penggunaan memori melebihi had. Carian di dalam klien masih berfungsi pada ruang kerja yang sedang anda buka.
Dua had perlu diketahui sebelum anda menjemput orang lain. Ruang kerja self-hosted dihadkan kepada maksimum 10 kerusi (seats), dan melebihi jumlah itu memerlukan lesen Team daripada AFFiNE. Storan blob tanpa had dan saiz blob tanpa had untuk instans self-hosted dinyatakan dalam dokumentasi sebagai perkara yang dirancang tetapi belum dilaksanakan sepenuhnya, disemak pada Ogos 2026. Kedua-dua perkara ini tidak menjejaskan isi rumah atau pasukan kecil. Namun, kedua-duanya penting jika anda merancang untuk memindahkan empat puluh orang.
Naik taraf
Baca nota keluaran terlebih dahulu, terutamanya bagi peningkatan versi minor seperti 0.26 ke 0.27, kerana perubahan yang memecahkan keserasian (breaking changes) sering berlaku. Sandarkan pangkalan data dan direktori storan sebelum anda melakukan sebarang perubahan, kerana proses migrasi akan mengubah skema anda pada permulaan seterusnya dan tiada cara untuk membatalkannya. Kemudian, tukar digest yang ditetapkan, jalankan docker compose pull diikuti dengan docker compose up -d, dan pantau docker compose logs -f affine_migration sehingga ia tamat dengan sempurna. docker image prune akan membersihkan lapisan lama selepas itu. Satu nota sejarah bagi sesiapa yang menggunakan pemasangan yang sangat lama: bermula dari versi 0.23.0, nama imej telah ditukar daripada affine-graphql kepada affine, jadi fail compose yang lebih lama daripada versi tersebut perlu ditulis semula pada baris imejnya sebelum proses pull dapat menemui sebarang data.
FAQ
Mengapa kontena AFFiNE tidak pernah bermula?
Servis affine mengisytiharkan condition: service_completed_successfully pada tugasan affine_migration, jadi jika migrasi keluar dengan sebarang status selain 0, pelayan tidak akan dimulakan dan tiada antara muka web yang muncul. Jalankan docker compose logs affine_migration untuk melihat langkah mana yang terhenti. Punca paling biasa pada fail compose yang disunting secara manual ialah penggunaan imej postgres standard menggantikan pgvector/pgvector:pg16, kerana skema AFFiNE mengisytiharkan sambungan pgvector dan mencipta jadual dengan lajur vector(1024) yang tidak boleh dibuat oleh Postgres biasa.
Berapakah RAM yang diperlukan oleh AFFiNE yang dihoskan sendiri?
Halaman keperluan AFFiNE meminta sekurang-kurangnya 4 teras CPU dan 2 GB RAM, meningkat kepada 4 GB apabila dokumen melebihi 10,000 perkataan, dan ia menyatakan bahawa penggabungan dokumen dengan 10,000 pengubahsuaian boleh memuncak sehingga 1 GB. Pada pelayan 2 GB, puncak itulah yang menyebabkan masalah, bukan beban melahu: pembunuh out-of-memory kernel menghentikan proses AFFiNE, dan restart: unless-stopped memulakannya semula, jadi pengguna melihat halaman dimuat semula dan bukannya ralat. Sahkan perkara ini dengan docker inspect affine_server --format '{{.State.OOMKilled}}' dan sudo dmesg -T | grep -i 'out of memory', kemudian tambah fail swap 2 GB supaya lonjakan menjadi perlahan dan bukannya membawa maut.
Di manakah AFFiNE menyimpan data saya, dan apakah yang perlu saya sandarkan?
Tiga laluan di bawah direktori compose anda menyimpan segala-galanya: ./data/postgres untuk pangkalan data, ./data/storage untuk fail yang dimuat naik, dan ./config untuk config.json. Sandarkan pangkalan data dengan docker compose exec -T postgres pg_dump --format c --username affine affine > affine.dump dan bukannya dengan menyalin fail, kerana Postgres yang sedang berjalan tidak boleh disalin dengan selamat. Tar ./data/storage untuk muat naik dan simpan salinan config.json secara manual, memandangkan eksport konfigurasi daripada panel admin disenaraikan sebagai belum dilaksanakan setakat Ogos 2026.
Adakah kolaborasi masa nyata berfungsi pada AFFiNE yang dihoskan sendiri?
Ya, dan tiada apa-apa yang perlu diaktifkan untuknya. Satu-satunya keperluan ialah reverse proxy anda, kerana penyelarasan berjalan melalui sambungan WebSocket. Pada nginx, ini bermakna proxy_http_version 1.1 berserta pengepala Upgrade dan Connection: upgrade, manakala Traefik dan Caddy melalukan sambungan tersebut tanpa konfigurasi tambahan. Gejala proksi yang tidak menaik taraf sambungan ini ialah ruang kerja yang dimuat dan dilog masuk seperti biasa, manakala suntingan yang dibuat dalam satu pelayar tidak pernah muncul dalam pelayar yang lain.
Bolehkah saya menjalankan AFFiNE dengan imej Postgres standard?
Tidak. schema.prisma AFFiNE mengisytiharkan extensions = [pgvector(map: "vector")] dan mentakrifkan empat jadual dengan lajur embedding jenis vector(1024), dan tugasan migrasi mencipta jadual tersebut walaupun ciri AI dimatikan. Gunakan pgvector/pgvector:pg16, iaitu Postgres 16 dengan sambungan tersebut dikompilasi bersama. Jika anda menghalakan AFFiNE ke pelayan Postgres luaran sebaliknya, pasang pgvector padanya dan cipta sambungan dalam pangkalan data sasaran sebelum menjalankan migrasi.