SSD Nodes Learn 🎉 VPS mula $5.50/buwan
Mga Gabay Matt ConnorNi Matt Connor · Na-update 2026-08-13

Paano mag-self-host ng AFFiNE gamit ang Docker Compose

I-run ang AFFiNE sa isang VPS gamit ang Docker Compose: alamin ang 4 na container, pinned image tags, data location, backups, at limitasyon ng 2 GB RAM.

Mga makukuha mo kapag nag-self-host ng AFFiNE

Sa pag-self-host ng AFFiNE, magkakaroon ka ng Notion-style workspace sa server na kontrolado mo. Tumatakbo ito bilang apat na container: ang application, isang one-shot migration job, Postgres, at Redis. Kasama ang real-time collaboration, hanggang sa 10 seat na default na itinatakda para sa isang self-hosted workspace. Ang installation ay binubuo ng isang compose file at isang JSON config file. Ang kailangang pag-isipan ay ang image tags, disk layout, memory ceiling, at ang proxy na ilalagay mo sa harap nito.

Pinagsasama ng AFFiNE ang document editor at infinite canvas sa iisang workspace. Kaya maaaring basahin ang isang page bilang dokumento o ilatag ito bilang whiteboard. Kung nagpapasya ka pa kung ano ang patatakbuhin, basahin muna ang paghahambing ng mga self-hosted na alternatibo sa Notion. Ipinapalagay ng gabay na ito na nakapili ka na. Tinutukan nito ang wastong pagpapatakbo ng AFFiNE sa halip na muling paghambingin ang mga opsyon.

Sinuri ang lahat ng nilalaman dito batay sa self-host documentation ng AFFiNE at sa mga published release file noong 8 August 2026. Ang pinakabagong stable release sa petsang iyon ay 0.27.3, na inilabas noong 23 July 2026.

Ano talaga ang ginagawa ng apat na container

affine ang server at web client sa iisang image. Nakikinig ito sa port 3010.

affine_migration ay isang one-shot job na nagpapatakbo ng node ./scripts/self-host-predeploy.js, nag-a-apply ng database migrations, at nag-e-exit. Idinedeklara ng application ang condition: service_completed_successfully bilang dependency ng job na iyon, kaya kapag nag-exit ang migration nang may non-zero status, hindi talaga nagsisimula ang affine. Kapag hindi lumalabas ang web interface, ang log ng job na iyon ang unang dapat basahin.

postgres ang naglalaman ng iyong mga dokumento, user, workspace, at permission. Ang shipped image ay pgvector/pgvector:pg16, na karaniwang Postgres 16 na may naka-compile na pgvector extension. Nagdaragdag ang pgvector ng vector column type sa Postgres. Ito ang numeric form na ginagamit para mag-store ng embeddings upang ma-search ang text batay sa kahulugan.

redis ang hard dependency: naghihintay ang server at migration job sa health check nito bago sila magsimula. Pansinin kung ano ang hindi ibinibigay ng shipped compose file sa Redis, na isang volume. Walang laman nito ang nananatili matapos ang docker compose down, kaya malinaw na wala itong iyong content at hindi ito nangangailangan ng backup.

Bakit pgvector ang Postgres image at hindi ang stock postgres

Ang requirement ay nagmumula sa schema ng AFFiNE, hindi sa preference. Sa schema.prisma, idinedeklara ng datasource ang extensions = [pgvector(map: "vector")], at may embedding column ang apat na table na naka-type bilang vector(1024). Ginagawa ng migration job ang mga table na ito kahit hindi mo kailanman i-enable ang AI features, kaya dapat umiiral na ang extension sa database bago matapos ang migration. Kapag pinalitan mo ito ng postgres:16, mawawala ang extension, hindi magagawa ng migration ang mga column na iyon, at mananatili ang server na naghihintay sa isang job na nag-fail.

Lumipat ang AFFiNE sa pgvector image sa version 0.21. Kung mas luma rito ang install, hindi sapat ang pag-edit sa image line para makumpleto ang upgrade. Basahin muna ang upgrade page sa self-host docs ng AFFiNE bago mag-pull ng anuman.

May isa pang mahalagang bagay tungkol sa tag na iyon. Ang pg16 ay nangangahulugang Postgres 16, at hindi basta numerong maaaring i-bump ang major version ng Postgres. Kapag pinalitan mo ito ng pg17 habang ginagamit ang kasalukuyang data directory, tatanggi ang Postgres na magsimula. Makikita ang linyang tulad ng The data directory was initialized by PostgreSQL version 16, which is not compatible with this version 17 sa docker compose logs postgres. Ang paglipat ng major version ay nangangailangan ng dump at restore sa isang bagong data directory.

Gaano karaming CPU at RAM ang kailangan ng self-hosted AFFiNE

Nangangailangan ang requirements page ng AFFiNE ng hindi bababa sa 4 na CPU core at 2 GB ng RAM. Itinataas nito ang kinakailangang memory sa 4 GB kapag lumampas sa 10,000 salita ang mga document mo. Ipinapaliwanag din ng parehong page kung saan napupunta ang memory: sa sync system at document merging. May isang figure itong dapat tandaan: ang pag-merge ng document na may 10,000 modification ay maaaring umabot sa 1 GB ang peak memory usage.

Ihambing ito sa 2 GB plan na ginagamit ng dalawang taong nagsusulat. Maayos ang average usage. Nananatili sa loob ng limit ang Postgres at Node process, at may natitirang memory. Ang peak usage ang problema. Maaaring humingi ang isang malaking merge ng karagdagang 1 GB bukod sa memory na ginagamit na. Sa 2 GB server na walang swap, tinutugunan ng out-of-memory (OOM) killer ng kernel ang request sa pamamagitan ng pag-kill sa pinakamalaking process, na siyang AFFiNE server.

Walang error na nakikita ang iyong kasamahan. Nakikita lamang nila na nag-reload ang page dahil awtomatikong ibinabalik ng restart: unless-stopped ang container sa loob ng ilang segundo. Huwag itong hulaan. Kumpirmahin ito:

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

Ang true mula sa unang command, o isang linya ng Killed process na naglalaman ng node mula sa ikalawang command, ay nangangahulugang naubusan ka ng memory at hindi nakatagpo ng bug. Ayusin ito mula sa dalawang panig. Magdagdag muna ng swap upang maging mabagal sa halip na fatal ang memory spike:

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

Dapat nang mag-report ang free -h ng kabuuang 2.0Gi swap. Hindi pinapabilis ng swap ang AFFiNE, at hindi iyon ang layunin nito. Ginagawa nitong mabagal na segundo ang isang one-second spike sa halip na mauwi sa dead container. Ang kabilang bahagi ng fix ay pigilan ang Postgres na palakihin ang cache nito hanggang sa magamit nito ang space na kailangan ng application kapag nagme-merge. Para rito ang memory limits sa isang Compose service.

Mas madaling tantiyahin ang storage. Ito ang mga figure na inilalathala ng AFFiNE sa parehong page:

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
  }
]

Kumokonsumo ng 1.5 GB ang server install. Ang 1,000 document na may humigit-kumulang 1,000 salita bawat isa ay nagdaragdag ng 0.1 GB ng Postgres data, na halos wala. Ang 1,000 uploaded file ay nagdaragdag ng 10 GB, at iyon ang pangunahing factor. Mga planning figure na inilathala ang mga ito, hindi mga sukat mula sa tumatakbong instance. Kaya ituring ang mga ito bilang gabay sa pattern ng paggamit, hindi bilang garantiya. Ang pattern ang mahalaga: mananatiling maliit ang database mo, at ang uploads ang tutukoy sa kailangan mong disk space.

Ikaw mismo ang sumulat ng compose file, at i-pin ang mga tag

Nagda-download ang dokumentadong installation ng handa nang file gamit ang curl -L -o docker-compose.yml https://github.com/toeverything/AFFiNE/releases/latest/download/docker-compose.yml. Gumagana iyon. May isang detalyeng dapat mong malaman bago ka umasa rito: noong 8 August 2026, binabasa pa rin ng file na naka-attach sa release 0.27.3 ang mga path nito mula sa .env file, gamit ang ${UPLOAD_LOCATION}, ${CONFIG_LOCATION}, at ${DB_DATA_LOCATION}. Samantala, ipinapakita ng reference page ng documentation ang mas bagong layout na inilalagay ang lahat sa ilalim ng ./data at hindi na nangangailangan ng .env. Parehong tama ang mga ito. Kapag ikaw mismo ang sumulat ng file, malinaw ang gagamiting layout. Kailangan mo rin itong i-edit para i-pin ang mga image at magtakda ng database password.

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

Awtomatikong binabasa ng Compose ang .env mula sa project directory at ipinapalit nito ang ${DB_PASSWORD} para sa iyo. Kaya hindi lumalabas ang password sa file na maaari mong i-paste sa isang support thread. Panatilihin ang ganitong kasanayan sa bawat stack na pinapatakbo mo. Ipinaliwanag ang dahilan sa pag-iwas na ilagay ang mga secret sa compose file.

Isulat ngayon ang ~/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

May apat na pagkakaiba ito kumpara sa file na inilalabas ng upstream, at bawat isa ay may dahilan.

  • 127.0.0.1:3010:3010 ang nagpa-publish ng port sa loopback address lamang. Kaya walang makaka-access sa AFFiNE mula sa labas ng server hangga't hindi mo pinipili kung paano ito ilalantad. Ang upstream na '3010:3010' ay nagba-bind sa bawat interface. Sa karamihan ng VPS image, kasama rito ang public interface.
  • Inalis ang POSTGRES_HOST_AUTH_METHOD: trust at nagtakda ng password. Tumatanggap ang trust authentication ng anumang koneksyon sa database na iyon bilang user na affine nang walang password. Limitado ito sa private Compose network. Ayos iyon hanggang sa mag-attach ka ng isa pang container sa network na iyon o mag-publish ng 5432 habang nagde-debug.
  • Pinalitan ng redis:8-alpine ang bare na redis, na nagre-resolve sa latest. Noong August 2026, Redis 8 iyon. Kaya pinapanatili ng pin ang major version na sinubukan mo at pinipigilan nitong dumating ang Redis 9 habang may ibang docker compose pull na isinasagawa.
  • Nanatiling eksaktong kapareho ng upstream ang pgvector/pgvector:pg16, ayon sa dahilang ipinaliwanag sa itaas.

Binabasa lamang ang POSTGRES_PASSWORD kapag unang ginagawa ng Postgres ang data directory nito. Kung mayroon nang instance, itakda ang password gamit ang docker compose exec postgres psql -U affine -c "ALTER USER affine WITH PASSWORD 'yourpassword'" at pagkatapos ay i-update ang DATABASE_URL upang tumugma.

Nasa config/config.json ang configuration

Binabasa ng AFFiNE ang mga setting nito mula sa config/config.json, na siyang directory na ni-mount mo sa /root/.affine/config. Walang awtomatikong gumagawa ng file na iyon, kaya kailangan mo itong isulat bago ang unang pagsisimula. Buksan ang ~/affine/config/config.json sa isang editor at ilagay ang content na ito. Gamitin ang sarili mong domain sa halip ng halimbawa:

{
  "$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
    }
  }
}

Ang server.externalUrl ay dapat na address na aktuwal na binubuksan ng mga user sa browser. Gumagawa ang AFFiNE ng mga share link at workspace invitation batay sa value na iyon. Kung mananatili ito sa http://localhost:3010, ituturo ng ipinadalang invitation ang recipient sa sarili nilang machine at mabibigo roon. Itakda ito sa public HTTPS address bago ang unang pagsisimula, upang hindi magkaiba ang nilalaman ng file at ng admin panel.

Kinokontrol ng copilot ang mga AI feature. Ang copilot.byok.enabled ay ang bring-your-own-key switch. Pinapayagan nito ang workspace owner na ilagay ang sarili nilang model provider key sa workspace settings. Walang kasamang AI subscription ang self-hosted AFFiNE. Iwanang false ang dalawang setting kung ayaw mong gamitin ito.

Simulan ang stack:

docker compose up -d
docker compose ps

Dapat ipakita ng docker compose ps na healthy ang affine_postgres at affine_redis, running ang affine_server, at may state na exited (0) ang affine_migration_job. Anumang ibang exit code sa migration job ang kailangang siyasatin. Nakasaad sa log nito kung aling step ang huminto:

docker compose logs affine_migration

I-pin ang image bago mo makalimutan

Ang stable ay isang gumagalaw na tag. Tinutukoy ng release workflow ng AFFiNE ang bawat stable build gamit ang ilang tag, at dalawa rito ang mahalaga: ang stable, na itinuturo sa bagong release sa bawat release, at ang stable- na sinusundan ng git short hash, na hindi nagbabago. Kapag iniwan sa stable, ang docker compose pull pagkalipas ng anim na buwan ay kukuha ng ibang image at magpapatakbo ng migrations laban sa database mo sa panahong hindi mo pinili. I-pin ang eksaktong image na sinubukan mo:

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

Magpi-print ito ng linyang tulad ng ghcr.io/toeverything/affine@sha256: na sinusundan ng mahabang hash. I-paste ang buong string sa linyang image: ng parehong affine at affine_migration. Dapat palaging magkapareho ang dalawang iyon dahil iisang image ang ginagamit nila sa magkaibang role, at nangangahulugan ang mismatch na imu-migrate ang database sa isang schema habang ibang schema naman ang ginagamit sa pag-serve nito. Sa ganitong paraan, sinasadya ang upgrade sa pamamagitan ng pag-edit sa halip na maging sorpresa: palitan ang digest, mag-back up, docker compose pull, docker compose up -d.

Gawin muna ang admin account bago ito magawa ng iba

Buksan ang /admin sa bagong instance. Dadalhin ka ng AFFiNE sa page para gumawa ng account dahil wala pang administrator ang server. Walang invitation code o setup token sa prosesong ito. Ang unang mag-load ng page na iyon ang magiging administrator ng server. Kaya dapat manatiling sarado ang port hanggang makapag-register ka.

Dahil dito, naka-bind ang compose file sa 127.0.0.1. I-access ito sa pamamagitan ng SSH tunnel mula sa sarili mong machine:

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

Hayaan itong tumakbo at buksan ang http://127.0.0.1:3010/admin sa local browser mo. Mag-register at mag-log in, pagkatapos ay isara ang tunnel. Saka pa lamang ligtas na ilagay ang instance sa public name.

Kung saan iniimbak ng AFFiNE ang iyong data

Tatlong path ang naglalaman ng lahat ng data, at nasa loob silang lahat ng directory na ginawa mo.

  • ./data/postgres ang Postgres data directory: mga document, user, workspace, at permission.
  • Naka-mount ang ./data/storage sa /root/.affine/storage sa loob ng container at naglalaman ito ng bawat in-upload na file.
  • Naka-mount ang ./config sa /root/.affine/config at naglalaman ito ng config.json.

Sa bahaging ito, bind mounts ang ginagamit ng upstream sa halip na named volumes. Sadyang ganoon ang pagpili: maaari mong i-tar at kopyahin ang mga path na ito gamit ang karaniwang mga command, nang hindi kailangang alamin sa Docker kung saan nito inilagay ang mga ito. Ang kapalit nito ay ikaw na ang kailangang mangasiwa sa file ownership sa host. Ito ang trade-off na tinatalakay sa bind mounts at named volumes.

Paano i-back up ang AFFiNE

Dalawang bagay ang kailangang i-back up, at magkaiba ang paraan ng pag-back up sa mga ito. Live server ang database, kaya magiging corrupt ang kopya kapag kinopya ang mga file nito habang tumatakbo ito. I-dump ito sa halip:

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/

Tumatakbo ang dump sa loob ng container gamit ang local socket nito, kaya hindi ito humihingi ng password. Suriin ang laki sa output ng ls. Kapag ilang daang byte lamang ang file, nabigo ang dump kahit ginawa pa rin ng shell ang file. Karaniwan itong natutuklasan pagkalipas ng anim na buwan. Mahalaga rin ang -T: kung wala ito, maaaring mag-allocate ang Compose ng terminal at ma-corrupt ang binary stream.

Mga file lamang ang mga na-upload na file, kaya i-tar ang mga ito:

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

Manu-manong isama ang config.json sa backup. Nakalista pa rin sa dokumentasyon ng AFFiNE na hindi pa ipinatutupad ang configuration export mula sa admin panel, batay sa pagsusuri noong August 2026, kaya ang file sa disk ang tanging kopya ng iyong settings. Kopyahin ang lahat ng tatlong file palabas ng server. Hindi backup ang backup na nasa parehong disk ng pinoprotektahan nitong data.

Pagpapanumbalik, at isang bitag sa mga inilathalang hakbang

Basahin ang opisyal na mga hakbang sa pagpapanumbalik bago mo ito kailanganin, at basahin ang mga ito nang mabuti. Sa bersyong inilathala noong August 2026, kinokopya nito ang file na pinangalanang affine.backup papunta sa container at pagkatapos ay nagpapanumbalik mula sa ./pg.backup. Magkaibang pangalan ang mga ito. Tinatanggal din nito ang directory na ./postgres, kahit nasa ./data/postgres ang data ng kasalukuyang compose file. Gamitin ang mga path na aktuwal mong ginamit, hindi ang mga nasa snippet. Narito ang sequence para sa layout na ginamit sa guide na ito:

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

Pansinin ang mv sa halip na rm. Ang pagpapanumbalik sa database na wala kang kopya ay maaaring magdulot ng total data loss dahil sa isang maling command. Wala namang gastos sa pansamantalang paglilipat sa lumang directory. I-restore din ang uploads gamit ang tar xzf backup/storage-2026-08-08.tgz -C data. Kung hindi, mali ang pag-render ng bawat document dahil sira ang mga attachment. Pagkatapos, mag-log in at magbukas ng document na may image. Iyan ang test. Ang restore na hindi mo pa nabubuksan sa browser ay file lamang, hindi backup.

Paglalagay ng AFFiNE sa likod ng proxy na ginagamit mo na

Gumagamit ang AFFiNE ng WebSocket, at hindi ito optional. Malinaw itong sinasabi sa documentation: WebSocket ang batayan ng AFFiNE sync at collaboration system. Kaya kapag hindi nag-u-upgrade ng mga connection ang proxy, magkakaroon ka ng workspace kung saan tahimik na tumitigil ang pag-sync ng mga edit. Naglo-load ang page at gumagana ang login, pero hindi nakakarating sa kabilang browser ang edit na ginawa sa isang browser. Sa developer tools ng browser, buksan ang Network tab at i-filter ito sa WS. Kapag paulit-ulit na nagbubukas at nagsasara ang connection, hindi ipinapasa ng proxy ang upgrade.

Kung gumagamit ka na ng Traefik para sa ibang container, idagdag ang AFFiNE bilang normal na service. Tanggalin ang ports: block mula sa affine service, pagkatapos ay idagdag ang:

    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'

Sa ibaba ng file, kasabay ng services:, idagdag ang:

networks:
  proxy:
    external: true

Dapat tumugma ang pangalan ng certificate resolver sa nakatakda sa iyong Traefik configuration. Ang loadbalancer.server.port ay ang container port 3010, hindi host port. Awtomatikong ipinapasa ng Traefik ang mga WebSocket connection at wala nang karagdagang configuration na kailangan. Kung ang iba mong app ay nasa likod na ng Authentik para sa single sign-on, maaaring gumamit ng forward auth middleware sa router na ito upang kontrolin ang browser access sa AFFiNE. Iwan muna itong naka-disable hanggang masubukan mo ang desktop app, dahil wala itong browser session at basta na lang mabibigo ang sync. Ang pagpapatakbo ng maraming app sa iisang instance nito ay nakasaad sa isang Traefik sa harap ng maraming app.

Sa nginx, kailangan mong tahasang hilingin ang 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;
}

Ang default na value ng client_max_body_size sa nginx ay 1 MB. Kung wala ang linyang iyon, mabibigo ang bawat upload na mas malaki sa maliit na larawan at magbabalik ito ng 413 status. Walang lalabas sa AFFiNE logs dahil hindi nakarating doon ang request. Isang linya lang ang kailangan sa Caddy, reverse_proxy http://127.0.0.1:3010, at awtomatiko nitong hina-handle ang certificates at WebSocket upgrades.

Mga hindi kasama sa self-hosted build

Maging tapat sa iyong sarili tungkol dito bago mo ilipat ang isang team.

May real-time collaboration, at ito ang feature na pinagtutuunan ng lahat ng sizing advice dahil ayon sa sariling documentation ng AFFiNE, ang memory use ay nagmumula sa sync system at sa document merging. Ang offline editing ang dahilan kung bakit maraming tao ang naghahanap ng local-first tool, at maaaring idagdag ng desktop application ang iyong self-hosted server sa workspace list nito at mag-log in dito. Subukan muna ang eksaktong offline behavior na kailangan ng iyong team bago ka magpasya: mag-edit sa desktop app habang naka-off ang network, kumonekta muli, at pagkatapos ay tingnan ang resulta sa pangalawang device. Hindi ebidensiya ang mga feature list, kabilang na ang isang ito.

Naka-off ang server-side full-text search sa shipped compose file, kung saan nakatakda ang AFFINE_INDEXER_ENABLED=false sa server at sa migration job. Para i-on ito, kailangan mong magdagdag ng Manticore Search container, na magiging ikalimang service at mangangailangan ng mas maraming memory. Sa isang 2 GB box, ito ang pagbabagong magtutulak sa iyo lampas sa limitasyon. Gumagana pa rin ang search sa loob ng client para sa workspace na kasalukuyan mong binuksan.

May dalawang limitasyong dapat mong malaman bago ka mag-imbita ng mga tao. Ang isang self-hosted workspace ay maaaring magkaroon ng hanggang 10 seat, at kailangan ng Team license mula sa AFFiNE kapag lumampas dito. Inilalarawan sa documentation na nakalaan ang unlimited blob storage at unlimited blob size para sa self-hosted instance, ngunit hindi pa ganap na naipatutupad ang mga ito, ayon sa pagsusuri noong August 2026. Walang epekto ang alinman sa dalawang ito sa isang household o maliit na team. Pareho itong mahalaga kung plano mong maglipat ng forty people.

Mga Upgrade

Basahin muna ang release notes, lalo na para sa minor version bump gaya ng 0.26 hanggang 0.27, dahil dito maaaring magkaroon ng breaking changes. Mag-back up ng database at storage directory bago gumawa ng anumang pagbabago, dahil binabago ng migration job ang schema sa susunod na pagsisimula at walang paraan para i-undo ito. Pagkatapos, palitan ang pinned digest, patakbuhin ang docker compose pull kasunod ang docker compose up -d, at i-monitor ang docker compose logs -f affine_migration hanggang sa malinis itong mag-exit. Nililinis naman ng docker image prune ang mga lumang layer pagkatapos. Isang mahalagang historical note para sa mga gumagamit ng napakalumang installation: simula sa version 0.23.0, nagbago ang image name mula affine-graphql patungong affine. Kaya kailangang baguhin ang image lines sa compose file na mas luma rito bago makahanap ng image ang pull.

FAQ

Bakit hindi kailanman nagsisimula ang AFFiNE container?

Idinedeklara ng affine service ang condition: service_completed_successfully sa affine_migration job. Kaya kung matatapos ang migration sa status na iba sa 0, hindi kailanman magsisimula ang server at walang lalabas na web interface. Patakbuhin ang docker compose logs affine_migration upang makita kung aling step ang huminto. Ang pinakakaraniwang sanhi sa mano-manong inedit na compose file ay ang paggamit ng stock na postgres image sa halip na pgvector/pgvector:pg16. Idinedeklara kasi ng AFFiNE schema ang pgvector extension at gumagawa ito ng mga table na may mga column na vector(1024) na hindi kayang gawin ng plain Postgres.

Gaano karaming RAM ang kailangan ng self-hosted AFFiNE?

Ayon sa requirements page ng AFFiNE, kailangan nito ng hindi bababa sa 4 CPU cores at 2 GB ng RAM. Tumataas ito sa 4 GB kapag lumampas sa 10,000 salita ang mga dokumento. Nakatala rin doon na maaaring umabot sa 1 GB ang peak kapag nag-merge ng dokumentong may 10,000 modification. Sa 2 GB na server, ang peak na ito ang nagdudulot ng problema, hindi ang idle load. Ihihinto ng kernel out-of-memory killer ang AFFiNE process, at muli itong sisimulan ng restart: unless-stopped. Kaya page reload ang nakikita ng mga user sa halip na error. Kumpirmahin ito gamit ang docker inspect affine_server --format '{{.State.OOMKilled}}' at sudo dmesg -T | grep -i 'out of memory'. Pagkatapos, magdagdag ng 2 GB swap file upang maging mabagal lamang ang spike sa halip na maging fatal.

Saan ini-store ng AFFiNE ang data ko, at ano ang kailangan kong i-back up?

May tatlong path sa ilalim ng compose directory na naglalaman ng lahat: ./data/postgres para sa database, ./data/storage para sa mga uploaded file, at ./config para sa config.json. I-back up ang database gamit ang docker compose exec -T postgres pg_dump --format c --username affine affine > affine.dump sa halip na kopyahin ang mga file. Hindi ligtas kopyahin ang mga file habang tumatakbo ang Postgres. I-tar ang ./data/storage para sa mga upload at manu-manong magtabi ng kopya ng config.json. Nakalista ang pag-export ng configuration mula sa admin panel bilang hindi pa na-implement hanggang August 2026.

Gumagana ba ang real-time collaboration sa self-hosted AFFiNE?

Oo, at walang kailangang i-enable para rito. Ang tanging requirement ay ang reverse proxy mo, dahil tumatakbo ang sync sa WebSocket connections. Sa nginx, nangangahulugan ito ng proxy_http_version 1.1 kasama ang Upgrade at Connection: upgrade headers. Ipinapasa naman ng Traefik at Caddy ang mga connection na ito nang walang karagdagang configuration. Ang palatandaan ng proxy na hindi nag-u-upgrade sa mga ito ay isang workspace na normal na naglo-load at nagla-login, pero hindi lumalabas sa isa pang browser ang mga edit na ginawa sa unang browser.

Maaari ko bang patakbuhin ang AFFiNE gamit ang stock Postgres image?

Hindi. Idinedeklara ng AFFiNE schema.prisma ang extensions = [pgvector(map: "vector")] at tumutukoy ito sa apat na table na may column na embedding na may type na vector(1024). Ginagawa ng migration job ang mga table na ito kahit naka-off ang AI features. Gamitin ang pgvector/pgvector:pg16, na Postgres 16 na may naka-compile na extension na iyon. Kung external Postgres server ang gagamitin ng AFFiNE, i-install ang pgvector dito at gawin ang extension sa target database bago patakbuhin ang migration.