Self-host AFFiNE: Workspace seperti Notion
Jalankan AFFiNE di satu VPS dengan Docker Compose: empat container, tag image yang dipatok, lokasi data, pencadangan, dan kapasitas nyata RAM 2 GB.
Yang Anda dapatkan saat melakukan self-hosting AFFiNE
Self-hosting AFFiNE memberi Anda workspace bergaya Notion pada server yang Anda kendalikan. AFFiNE berjalan sebagai empat container: aplikasi, job migrasi sekali jalan, Postgres, dan Redis. Kolaborasi real-time sudah tersedia, hingga 10 pengguna yang secara default didukung oleh workspace self-hosted. Instalasinya hanya memerlukan satu file compose dan satu file konfigurasi JSON. Hal yang perlu dipertimbangkan adalah tag image, tata letak disk, batas memori, dan proxy yang ditempatkan di depannya.
AFFiNE menggabungkan editor dokumen dan kanvas tanpa batas dalam workspace yang sama. Dengan demikian, satu halaman dapat dibaca sebagai dokumen atau dibentangkan sebagai papan tulis. Jika Anda masih menentukan aplikasi yang akan dijalankan, baca perbandingan alternatif Notion self-hosted terlebih dahulu. Panduan ini mengasumsikan bahwa pilihan tersebut sudah ditetapkan. Fokusnya adalah menjalankan AFFiNE dengan benar, bukan membandingkannya lagi.
Semua informasi di sini telah diperiksa berdasarkan dokumentasi self-host AFFiNE dan file rilis yang dipublikasikan pada 8 August 2026. Rilis stabil terbaru pada tanggal tersebut adalah 0.27.3, yang dipublikasikan pada 23 July 2026.
Apa yang sebenarnya dilakukan keempat container
affine adalah server dan klien web dalam satu image. Container ini mendengarkan pada port 3010.
affine_migration adalah job sekali jalan yang menjalankan node ./scripts/self-host-predeploy.js, menerapkan migrasi database, lalu berhenti. Aplikasi mendeklarasikan condition: service_completed_successfully pada job tersebut. Karena itu, migrasi yang berhenti dengan status non-zero berarti affine sama sekali tidak akan dimulai. Jika antarmuka web tidak dapat diakses, log job tersebut adalah hal pertama yang harus dibaca.
postgres menyimpan dokumen, pengguna, workspace, dan permission Anda. Image yang disediakan adalah pgvector/pgvector:pg16, yaitu Postgres 16 biasa dengan ekstensi pgvector yang sudah dikompilasi di dalamnya. pgvector menambahkan tipe kolom vector ke Postgres. Tipe ini menyimpan embedding dalam bentuk numerik agar teks dapat dicari berdasarkan maknanya.
redis adalah dependensi wajib. Server dan job migrasi sama-sama menunggu health check-nya sebelum mulai. Perhatikan bahwa file compose yang disediakan tidak memberikan volume kepada Redis. Tidak ada data di dalamnya yang bertahan setelah docker compose down. Ini menunjukkan bahwa Redis tidak menyimpan konten milik Anda dan tidak memerlukan backup.
Mengapa image Postgres yang digunakan adalah pgvector, bukan postgres standar
Persyaratan ini berasal dari schema AFFiNE, bukan preferensi. Dalam schema.prisma, datasource mendeklarasikan extensions = [pgvector(map: "vector")], dan empat tabel memiliki kolom embedding bertipe vector(1024). Job migrasi membuat tabel-tabel tersebut terlepas dari apakah Anda pernah mengaktifkan fitur AI. Karena itu, extension harus sudah tersedia di database sebelum migrasi dapat selesai. Jika Anda menggantinya dengan postgres:16, extension tersebut hilang, migrasi tidak dapat membuat kolom-kolom itu, dan server akan terus menunggu job yang sudah gagal.
AFFiNE beralih ke image pgvector pada versi 0.21. Pada instalasi yang lebih lama dari versi tersebut, mengedit baris image saja tidak cukup untuk melakukan upgrade. Baca halaman upgrade dalam dokumentasi self-host AFFiNE sebelum melakukan pull.
Ada satu hal lagi tentang tag tersebut. pg16 berarti Postgres 16, dan versi major Postgres bukan angka yang dapat dinaikkan begitu saja. Jika Anda mengubahnya menjadi pg17 pada data directory yang sudah ada, Postgres menolak untuk start, dengan baris seperti The data directory was initialized by PostgreSQL version 16, which is not compatible with this version 17 di docker compose logs postgres. Perpindahan versi major memerlukan dump dan restore ke data directory baru.
Kebutuhan CPU dan RAM untuk self-hosted AFFiNE
Halaman persyaratan AFFiNE meminta setidaknya 4 core CPU dan 2 GB RAM, lalu menaikkan kebutuhan memori menjadi 4 GB setelah dokumen Anda melebihi 10.000 kata. Halaman yang sama menjelaskan penggunaan memori tersebut: sistem sinkronisasi dan penggabungan dokumen. Halaman itu juga mencantumkan satu angka yang perlu diingat, yaitu penggabungan dokumen dengan 10.000 modifikasi dapat menggunakan hingga 1 GB.
Sekarang bandingkan angka tersebut dengan paket 2 GB yang digunakan oleh dua orang untuk menulis. Penggunaan rata-rata tidak bermasalah. Postgres dan proses Node tetap berada di bawah batas tersebut dengan sisa kapasitas. Masalahnya adalah penggunaan puncak. Satu penggabungan besar dapat meminta tambahan 1 GB di atas seluruh memori yang sudah digunakan. Pada server 2 GB tanpa swap, kernel menjawab permintaan tersebut dengan menjalankan out-of-memory (OOM) killer, yang menghentikan proses terbesar, yaitu server AFFiNE.
Rekan Anda tidak melihat pesan error. Mereka melihat halaman dimuat ulang karena restart: unless-stopped menghidupkan kembali container dalam hitungan detik. Jangan menebak penyebabnya. Konfirmasikan dengan perintah berikut:
docker inspect affine_server --format '{{.State.OOMKilled}} {{.RestartCount}}'
sudo dmesg -T | grep -i -E 'out of memory|killed process'true dari perintah pertama, atau baris Killed process yang mencantumkan node dari perintah kedua, berarti memori Anda habis, bukan terdapat bug. Atasi masalah ini dari kedua sisi. Tambahkan swap terlebih dahulu agar lonjakan penggunaan menjadi lambat, bukan fatal:
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 sekarang seharusnya melaporkan total swap sebesar 2.0Gi. Swap tidak membuat AFFiNE berjalan cepat, dan memang bukan itu tujuannya. Swap mengubah lonjakan penggunaan selama satu detik menjadi satu detik yang lambat, bukan container yang mati. Sisi lain dari perbaikan ini adalah mencegah Postgres memperbesar cache hingga memenuhi ruang yang diperlukan aplikasi saat penggabungan. Untuk itulah batas memori pada service Compose digunakan.
Kebutuhan penyimpanan jauh lebih mudah diperkirakan. Berikut angka yang dipublikasikan 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
}
]Instalasi server menggunakan 1.5 GB. Seribu dokumen dengan masing-masing sekitar seribu kata menambahkan 0.1 GB data Postgres, yang hampir tidak berarti. Seribu file yang diunggah menambahkan 10 GB, dan itulah faktor utamanya. Angka ini merupakan angka perencanaan yang dipublikasikan, bukan hasil pengukuran dari instance yang sedang berjalan. Jadi, gunakan angka tersebut untuk memahami polanya, bukan sebagai jaminan. Polanya yang penting: database Anda tetap kecil, sedangkan file yang diunggah menentukan kebutuhan disk.
Tulis sendiri file compose dengan tag yang dikunci
Instalasi yang didokumentasikan mengunduh file siap pakai dengan curl -L -o docker-compose.yml https://github.com/toeverything/AFFiNE/releases/latest/download/docker-compose.yml. Cara itu berfungsi. Namun, ada satu detail yang perlu diketahui sebelum Anda mengandalkannya: per 8 August 2026, file yang dilampirkan pada release 0.27.3 masih membaca path dari file .env menggunakan ${UPLOAD_LOCATION}, ${CONFIG_LOCATION}, dan ${DB_DATA_LOCATION}, sedangkan halaman referensi dalam dokumentasi menampilkan tata letak yang lebih baru. Tata letak tersebut menyimpan semuanya di bawah ./data dan sama sekali tidak memerlukan .env. Keduanya valid. Dengan menulis file sendiri, masalah ini terselesaikan. Anda juga memang harus mengeditnya untuk mengunci image dan menetapkan password database.
mkdir -p ~/affine/config ~/affine/data
cd ~/affine
printf 'DB_PASSWORD=%s\n' "$(openssl rand -hex 24)" > .env
chmod 600 .envCompose membaca .env dari direktori project secara otomatis dan menggantikan ${DB_PASSWORD} untuk Anda. Dengan demikian, password tidak muncul dalam file yang mungkin Anda tempelkan ke thread dukungan. Kebiasaan ini sebaiknya diterapkan pada setiap stack yang Anda jalankan. Alasannya dijelaskan dalam menyimpan secret di luar file 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-stoppedAda empat perbedaan dari file yang dirilis upstream. Masing-masing memiliki alasan.
127.0.0.1:3010:3010memublikasikan port hanya pada alamat loopback. Dengan demikian, tidak ada pihak di luar server yang dapat mengakses AFFiNE sampai Anda menentukan caranya.'3010:3010'dari upstream mengikat semua interface. Pada sebagian besar image VPS, ini mencakup interface publik.POSTGRES_HOST_AUTH_METHOD: trustdihapus dan password ditetapkan sebagai gantinya. Autentikasi trust menerima koneksi apa pun ke database tersebut sebagai useraffinetanpa password. Konfigurasi ini terbatas pada jaringan Compose privat. Namun, hal itu dapat menjadi masalah ketika Anda menambahkan container lain ke jaringan tersebut atau memublikasikan 5432 saat melakukan debugging.redis:8-alpinemenggantikanredistanpa tag, yang mengarah kelatest. Per August 2026, versi tersebut adalah Redis 8. Penguncian ini mempertahankan major version yang telah Anda uji dan mencegah Redis 9 terpasang saatdocker compose pullyang tidak terkait.pgvector/pgvector:pg16tetap persis seperti yang ditetapkan upstream, karena alasan yang dijelaskan di atas.
POSTGRES_PASSWORD hanya dibaca ketika Postgres membuat direktori datanya untuk pertama kali. Pada instance yang sudah ada, tetapkan password dengan docker compose exec postgres psql -U affine -c "ALTER USER affine WITH PASSWORD 'yourpassword'", lalu perbarui DATABASE_URL agar sesuai.
Konfigurasi berada di config/config.json
AFFiNE membaca pengaturannya dari config/config.json, yaitu direktori yang Anda mount pada /root/.affine/config. Tidak ada proses yang membuat file tersebut secara otomatis, jadi buat file itu sebelum start pertama. Buka ~/affine/config/config.json dalam editor dan isi dengan konten berikut. Ganti domain contoh 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 harus berupa alamat yang benar-benar dibuka pengguna di browser. AFFiNE membuat tautan berbagi dan undangan workspace berdasarkan nilai tersebut. Jika nilainya dibiarkan sebagai http://localhost:3010, undangan yang Anda kirim akan mengarahkan penerima ke mesin mereka sendiri dan gagal di sana. Tetapkan alamat HTTPS publik sebelum start pertama agar file konfigurasi dan panel admin selalu menggunakan alamat yang sama.
copilot mengontrol fitur AI. copilot.byok.enabled adalah opsi bring-your-own-key yang memungkinkan pemilik workspace memasukkan kunci provider model mereka sendiri dalam pengaturan workspace. Self-hosting AFFiNE tidak menyertakan langganan AI. Biarkan keduanya pada false jika Anda tidak menginginkannya.
Start stack:
docker compose up -d
docker compose psdocker compose ps seharusnya menampilkan affine_postgres dan affine_redis sebagai healthy, affine_server sebagai running, serta affine_migration_job dengan status exited (0). Kode keluar apa pun pada job migrasi selain yang diharapkan perlu diperiksa. Log job tersebut menunjukkan langkah yang berhenti:
docker compose logs affine_migrationTetapkan image sebelum lupa
stable adalah tag yang dapat berubah. Alur rilis AFFiNE menunjuk beberapa tag ke setiap build stabil, dan dua di antaranya penting di sini: stable, yang diarahkan ulang pada setiap rilis, dan stable- yang diikuti hash singkat git, yang tidak berubah. Jika tetap menggunakan stable, docker compose pull enam bulan dari sekarang akan mengambil image yang berbeda dan menjalankan migrasinya terhadap database pada waktu yang tidak Anda tentukan. Tetapkan image persis yang telah Anda uji:
docker compose pull
docker image inspect ghcr.io/toeverything/affine:stable --format '{{index .RepoDigests 0}}'Perintah tersebut mencetak baris seperti ghcr.io/toeverything/affine@sha256: yang diikuti hash panjang. Salin seluruh string tersebut ke baris image: pada keduanya, yaitu affine dan affine_migration. Keduanya harus selalu sama karena merupakan image yang sama dengan dua peran berbeda. Ketidaksesuaian berarti database dimigrasikan ke satu skema, tetapi dilayani dengan skema lain. Dengan demikian, upgrade menjadi perubahan yang dilakukan secara sengaja, bukan kejutan: ubah digest, buat cadangan, docker compose pull, docker compose up -d.
Buat akun admin sebelum orang lain melakukannya
Buka /admin pada instance baru. AFFiNE akan mengarahkan Anda ke halaman pembuatan akun karena server belum memiliki administrator. Alur ini tidak menggunakan kode undangan atau token penyiapan. Orang pertama yang memuat halaman tersebut akan menjadi administrator server Anda. Karena itu, port harus tetap tertutup sampai Anda selesai mendaftar.
Itulah alasan file compose di atas mengikat layanan ke 127.0.0.1. Akses melalui tunnel SSH dari komputer Anda sendiri:
ssh -L 3010:127.0.0.1:3010 you@your-server-ipBiarkan proses tersebut tetap berjalan, lalu buka http://127.0.0.1:3010/admin pada browser lokal Anda. Daftar dan login, kemudian tutup tunnel. Setelah itu, instance baru aman dipublikasikan menggunakan nama publik.
Lokasi AFFiNE menyimpan data
Tiga path menyimpan seluruh data, dan semuanya berada di dalam direktori yang Anda buat.
./data/postgresadalah direktori data Postgres yang berisi dokumen, pengguna, workspace, dan izin../data/storagedi-mount ke/root/.affine/storagedi dalam container dan menyimpan semua file yang diunggah../configdi-mount ke/root/.affine/configdan menyimpanconfig.json.
Upstream menggunakan bind mount di sini, bukan named volume. Pilihan ini disengaja: Anda dapat membuat arsip tar dan menyalin path tersebut menggunakan perintah biasa, tanpa harus meminta Docker menunjukkan lokasi penyimpanannya. Konsekuensinya, kepemilikan file pada host menjadi tanggung jawab Anda. Ini adalah trade-off yang dibahas dalam bind mount dan named volume.
Cara mencadangkan AFFiNE
Ada dua hal yang perlu dicadangkan, dan cara pencadangannya berbeda. Database adalah server aktif, sehingga menyalin filenya saat server berjalan akan menghasilkan salinan yang rusak. Gunakan 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 dijalankan di dalam container melalui socket lokalnya, sehingga tidak meminta password. Periksa ukurannya pada output ls tersebut. File berukuran beberapa ratus byte berarti dump gagal, tetapi shell tetap membuat filenya. Kegagalan ini sering baru diketahui enam bulan kemudian. -T juga penting: tanpa opsi tersebut, Compose dapat mengalokasikan terminal dan merusak aliran biner.
File yang diunggah hanyalah file biasa, jadi arsipkan dengan tar:
tar czf backup/storage-$(date +%F).tgz -C data storage
cp config/config.json backup/config-$(date +%F).jsonSimpan config.json dalam backup secara manual. Dokumentasi AFFiNE masih mencantumkan bahwa ekspor konfigurasi dari panel admin belum diimplementasikan, berdasarkan pemeriksaan pada Agustus 2026. Jadi, file pada disk adalah satu-satunya salinan pengaturan Anda. Salin ketiga file tersebut ke luar server. Backup yang disimpan pada disk yang sama dengan data yang dilindunginya bukanlah backup.
Pemulihan dan satu jebakan dalam langkah yang dipublikasikan
Baca langkah pemulihan resmi sebelum Anda membutuhkannya, dan baca dengan teliti. Dalam versi yang dipublikasikan pada Agustus 2026, langkah tersebut menyalin file bernama affine.backup ke dalam container lalu melakukan pemulihan dari ./pg.backup. Keduanya adalah nama yang berbeda. Langkah tersebut juga menghapus direktori ./postgres, sedangkan file compose saat ini menyimpan datanya di ./data/postgres. Gunakan path yang benar-benar Anda gunakan, bukan path dalam cuplikan. Berikut urutannya untuk layout 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 penggunaan mv, bukan rm. Memulihkan database yang salinannya tidak Anda simpan dapat mengubah satu perintah yang salah menjadi kehilangan data total. Memindahkan direktori lama ke lokasi lain tidak menimbulkan biaya. Pulihkan juga upload dengan tar xzf backup/storage-2026-08-08.tgz -C data. Jika tidak, setiap dokumen akan menampilkan lampiran yang rusak. Kemudian login dan buka dokumen yang berisi gambar. Itulah pengujiannya. Pemulihan yang belum Anda buka di browser masih berupa file, bukan backup.
Menempatkan AFFiNE di belakang proxy yang sudah Anda jalankan
AFFiNE menggunakan WebSocket, dan fitur ini wajib tersedia. Dokumentasinya menjelaskannya secara tegas: WebSocket adalah dasar sistem sinkronisasi dan kolaborasi AFFiNE. Proxy yang tidak melakukan upgrade koneksi tersebut akan menghasilkan workspace yang berhenti melakukan sinkronisasi tanpa pesan. Halaman tetap dimuat, login tetap berfungsi, tetapi perubahan yang dibuat di satu browser tidak pernah sampai ke browser lain. Buka tab Network di developer tools browser Anda, lalu filter ke WS. Koneksi yang berulang kali terbuka lalu tertutup menunjukkan bahwa proxy tidak meneruskan upgrade.
Jika Anda sudah menjalankan Traefik untuk container lain, AFFiNE dapat ditambahkan sebagai service biasa. Hapus blok ports: dari service affine, lalu tambahkan:
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'Di bagian bawah file, sejajar dengan services:, tambahkan:
networks:
proxy:
external: trueNama certificate resolver harus sama dengan yang ditentukan dalam konfigurasi Traefik, dan loadbalancer.server.port adalah port container 3010, bukan port host. Traefik mem-proxy koneksi WebSocket tanpa konfigurasi tambahan, jadi tidak ada yang perlu ditambahkan lagi. Jika stack Anda yang lain sudah berada di belakang Authentik untuk single sign-on, middleware forward auth pada router ini dapat membatasi akses browser ke AFFiNE. Namun, biarkan middleware tersebut nonaktif sampai Anda menguji aplikasi desktop, karena aplikasi desktop tidak memiliki sesi browser dan akan gagal melakukan sinkronisasi. Cara menjalankan beberapa aplikasi di belakang satu instance Traefik dibahas dalam satu Traefik di depan beberapa aplikasi.
Pada nginx, Anda harus meminta upgrade 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 secara default bernilai 1 MB di nginx. Tanpa baris tersebut, setiap upload yang lebih besar daripada foto kecil akan gagal dengan status 413, dan tidak ada apa pun yang muncul dalam log AFFiNE karena request tersebut tidak pernah sampai ke aplikasi. Caddy memerlukan satu baris, reverse_proxy http://127.0.0.1:3010, dan menangani sertifikat serta upgrade WebSocket secara otomatis.
Hal yang tidak tersedia dalam build self-hosted
Pahami hal ini sebelum memindahkan sebuah tim.
Kolaborasi real-time tersedia. Fitur inilah yang menjadi dasar seluruh rekomendasi sizing, karena dokumentasi AFFiNE sendiri menyebutkan bahwa penggunaan memori berasal dari sistem sinkronisasi dan penggabungan dokumen. Pengeditan offline adalah alasan banyak orang memilih tool local-first. Aplikasi desktop dapat menambahkan server self-hosted ke daftar workspace dan melakukan login ke server tersebut. Uji perilaku offline yang benar-benar dibutuhkan tim sebelum memutuskan: edit dokumen di aplikasi desktop saat jaringan dinonaktifkan, sambungkan kembali jaringan, lalu periksa hasilnya pada perangkat kedua. Daftar fitur bukan bukti. Hal ini juga berlaku untuk daftar ini.
Pencarian full-text di sisi server dinonaktifkan dalam file compose yang disertakan. Pada file tersebut, AFFINE_INDEXER_ENABLED=false ditetapkan pada server dan job migrasi. Untuk mengaktifkannya, Anda harus menambahkan container Manticore Search. Ini menjadi service kelima dan membutuhkan lebih banyak memori. Pada server dengan RAM 2 GB, perubahan inilah yang dapat membuat penggunaan sumber daya melewati batas. Pencarian di dalam client tetap berfungsi pada workspace yang sedang dibuka.
Ada dua batasan yang perlu diketahui sebelum Anda mengundang pengguna. Workspace self-hosted mendapat paling banyak 10 seat. Jika jumlahnya lebih dari itu, Anda memerlukan lisensi Team dari AFFiNE. Penyimpanan blob tanpa batas dan ukuran blob tanpa batas untuk instance self-hosted dijelaskan dalam dokumentasi sebagai fitur yang direncanakan, tetapi belum sepenuhnya diimplementasikan saat diperiksa pada August 2026. Keduanya tidak penting untuk rumah tangga atau tim kecil. Keduanya menjadi penting jika Anda berencana memindahkan empat puluh orang.
Pembaruan
Baca catatan rilis terlebih dahulu, terutama untuk peningkatan versi minor seperti 0.26 ke 0.27, karena perubahan yang tidak kompatibel dapat diterapkan. Cadangkan database dan direktori penyimpanan sebelum melakukan perubahan apa pun, karena tugas migrasi mengubah skema saat aplikasi dijalankan berikutnya dan perubahan tersebut tidak dapat dibatalkan. Selanjutnya, ubah digest yang dipatok, jalankan docker compose pull lalu docker compose up -d, dan pantau docker compose logs -f affine_migration sampai selesai tanpa error. docker image prune menghapus layer lama setelahnya. Catatan historis bagi pengguna instalasi yang sangat lama: mulai versi 0.23.0, nama image berubah dari affine-graphql menjadi affine. Karena itu, baris image pada file compose yang lebih lama dari versi tersebut harus ditulis ulang sebelum pull dapat menemukan image yang diperlukan.
FAQ
Mengapa container AFFiNE tidak pernah berjalan?
Service affine mendeklarasikan condition: service_completed_successfully pada job affine_migration. Karena itu, jika migrasi berakhir dengan status selain 0, server tidak pernah dijalankan dan antarmuka web sama sekali tidak muncul. Jalankan docker compose logs affine_migration untuk melihat langkah yang berhenti. Penyebab paling umum pada file compose yang diedit manual adalah penggunaan image postgres bawaan sebagai pengganti pgvector/pgvector:pg16. Schema AFFiNE mendeklarasikan ekstensi pgvector dan membuat tabel dengan kolom vector(1024) yang tidak dapat dibuat oleh Postgres biasa.
Berapa banyak RAM yang dibutuhkan AFFiNE yang di-host sendiri?
Halaman persyaratan AFFiNE meminta setidaknya 4 core CPU dan 2 GB RAM. Kebutuhannya meningkat menjadi 4 GB ketika dokumen melebihi 10,000 kata. Halaman tersebut juga mencatat bahwa penggabungan dokumen dengan 10,000 modifikasi dapat menggunakan hingga 1 GB RAM. Pada server 2 GB, penggunaan puncak inilah yang menyebabkan masalah, bukan beban saat idle. Kernel out-of-memory killer menghentikan proses AFFiNE, lalu restart: unless-stopped menjalankannya kembali. Akibatnya, pengguna melihat halaman dimuat ulang, bukan pesan error. Konfirmasikan kondisi ini dengan docker inspect affine_server --format '{{.State.OOMKilled}}' dan sudo dmesg -T | grep -i 'out of memory'. Kemudian, tambahkan file swap 2 GB agar lonjakan penggunaan RAM hanya memperlambat sistem, bukan menghentikannya.
Di mana AFFiNE menyimpan data saya, dan apa yang harus dicadangkan?
Tiga path di bawah direktori compose menyimpan seluruh data: ./data/postgres untuk database, ./data/storage untuk file yang diunggah, dan ./config untuk config.json. Cadangkan database dengan docker compose exec -T postgres pg_dump --format c --username affine affine > affine.dump, bukan dengan menyalin file, karena Postgres yang sedang berjalan tidak dapat disalin dengan aman. Gunakan Tar pada ./data/storage untuk file yang diunggah. Simpan salinan config.json secara manual karena ekspor konfigurasi dari panel admin tercatat belum diimplementasikan hingga August 2026.
Apakah kolaborasi real-time berfungsi pada AFFiNE yang di-host sendiri?
Ya, dan tidak ada pengaturan yang perlu diaktifkan. Satu-satunya persyaratan adalah reverse proxy Anda karena sinkronisasi berjalan melalui koneksi WebSocket. Pada nginx, ini berarti proxy_http_version 1.1 serta header Upgrade dan Connection: upgrade. Traefik dan Caddy meneruskan koneksi tersebut tanpa konfigurasi tambahan. Jika proxy tidak melakukan upgrade koneksi, gejalanya adalah workspace dimuat dan login berjalan normal, tetapi perubahan yang dibuat di satu browser tidak pernah muncul di browser lain.
Apakah AFFiNE dapat dijalankan dengan image Postgres bawaan?
Tidak. schema.prisma milik AFFiNE mendeklarasikan extensions = [pgvector(map: "vector")] dan mendefinisikan empat tabel dengan kolom embedding bertipe vector(1024). Job migrasi membuat tabel tersebut meskipun fitur AI dinonaktifkan. Gunakan pgvector/pgvector:pg16, yaitu Postgres 16 dengan ekstensi tersebut yang sudah dikompilasi di dalamnya. Jika Anda mengarahkan AFFiNE ke server Postgres eksternal, instal pgvector pada server tersebut dan buat ekstensi itu di database target sebelum menjalankan migrasi.