SSD Nodes Learn 🎉 VPS $5.50/ماہ سے
تعلیمی Matt Connorتحریر: Matt Connor · اپ ڈیٹ شدہ 2026-08-13

AFFiNE خود host کریں: Docker Compose مکمل رہنما

AFFiNE کو ایک VPS پر Docker Compose سے چلائیں: 4 containers، pinned image tags، data کی جگہ، backups، اور 2 GB RAM میں عملی طور پر کیا ممکن ہے۔

خود AFFiNE کی میزبانی کرنے پر آپ کو کیا ملتا ہے

AFFiNE کو خود host کرنے سے آپ کو اپنے زیرِ انتظام server پر Notion طرز کا workspace ملتا ہے۔ یہ چار containers کے طور پر چلتا ہے: application، one-shot migration job، Postgres، اور Redis۔ Real-time collaboration بھی شامل ہے، اور self-hosted workspace میں default طور پر زیادہ سے زیادہ 10 seats ہوتی ہیں۔ Installation کے لیے ایک compose file اور ایک JSON config file درکار ہوتی ہے۔ جن امور پر غور ضروری ہے ان میں image tags، disk layout، memory ceiling، اور سامنے رکھا جانے والا proxy شامل ہیں۔

AFFiNE ایک ہی workspace میں document editor اور infinite canvas فراہم کرتا ہے۔ اس لیے ایک page کو document کے طور پر پڑھا جا سکتا ہے یا whiteboard کی طرح پھیلایا جا سکتا ہے۔ اگر آپ ابھی یہ طے کر رہے ہیں کہ کیا چلانا ہے تو پہلے self-hosted Notion متبادلات کا موازنہ پڑھیں۔ یہ guide فرض کرتی ہے کہ انتخاب ہو چکا ہے، اور دوبارہ موازنہ کرنے کے بجائے AFFiNE کو درست طریقے سے چلانے کا طریقہ بیان کرتی ہے۔

یہاں موجود تمام معلومات AFFiNE کی self-host documentation اور 8 August 2026 کو شائع شدہ release files کے مطابق جانچی گئی ہیں۔ اس تاریخ کو تازہ ترین stable release 0.27.3 تھی، جو 23 July 2026 کو شائع ہوئی تھی۔

چار کنٹینرز عملاً کیا کرتے ہیں

affine ایک ہی image میں server اور web client فراہم کرتا ہے۔ یہ port 3010 پر listening کرتا ہے۔

affine_migration ایک one-shot job ہے جو node ./scripts/self-host-predeploy.js چلاتا ہے، database migrations نافذ کرتا ہے، اور پھر exit ہو جاتا ہے۔ application اس job پر condition: service_completed_successfully کا اعلان کرتی ہے، اس لیے non-zero status کے ساتھ ختم ہونے والی migration کا مطلب ہے کہ affine کبھی start ہی نہیں ہوتا۔ جب web interface دستیاب نہ ہو تو سب سے پہلے اسی job کا log دیکھیں۔

postgres آپ کی documents، users، workspaces اور permissions محفوظ رکھتا ہے۔ فراہم کردہ image pgvector/pgvector:pg16 ہے، جو pgvector extension کے ساتھ compile کیا گیا معمول کا Postgres 16 ہے۔ pgvector، Postgres میں vector column type شامل کرتا ہے۔ یہ embeddings محفوظ کرنے کے لیے استعمال ہونے والی عددی صورت ہے، تاکہ text کو اس کے مفہوم کے مطابق search کیا جا سکے۔

redis ایک سخت dependency ہے: server اور migration job، دونوں start ہونے سے پہلے اس کے health check کا انتظار کرتے ہیں۔ غور کریں کہ فراہم کردہ compose file Redis کو کیا نہیں دیتی: volume۔ اس کے اندر موجود کوئی چیز docker compose down کے بعد برقرار نہیں رہتی۔ اس سے واضح ہے کہ اس میں آپ کا کوئی content محفوظ نہیں ہوتا اور اس کا backup ضروری نہیں ہے۔

Postgres image postgres:16 کیوں ہے، stock postgres کیوں نہیں

یہ ضرورت ترجیح کی وجہ سے نہیں بلکہ AFFiNE کے schema کی وجہ سے ہے۔ schema.prisma میں datasource، extensions = [pgvector(map: "vector")] کا اعلان کرتا ہے، اور چار tables میں embedding column موجود ہیں جن کی type vector(1024) ہے۔ migration job یہ tables اس وقت بھی بناتی ہے جب آپ AI features کبھی فعال نہ کریں۔ اس لیے migration مکمل ہونے سے پہلے database میں extension کا موجود ہونا ضروری ہے۔ postgres:16 کی جگہ کوئی دوسرا image استعمال کرنے سے extension ختم ہو جاتی ہے، migration یہ columns نہیں بنا سکتی، اور server ایسے job کا انتظار کرتا رہتا ہے جو ناکام ہو چکی ہے۔

AFFiNE نے version 0.21 میں pgvector image استعمال کرنا شروع کیا۔ اگر installation اس سے پرانی version پر ہے تو صرف image line میں ترمیم کرنا مکمل upgrade نہیں ہے۔ کچھ pull کرنے سے پہلے AFFiNE self-host docs میں upgrade page پڑھیں۔

اس tag کے بارے میں ایک اور اہم بات ہے۔ pg16 کا مطلب Postgres 16 ہے، اور Postgres major version ایسی number نہیں جسے صرف بڑھایا جا سکے۔ موجودہ data directory پر اسے pg17 میں تبدیل کرنے سے Postgres start ہونے سے انکار کر دیتا ہے، اور docker compose logs postgres میں The data directory was initialized by PostgreSQL version 16, which is not compatible with this version 17 جیسی line دکھائی دیتی ہے۔ Major version منتقل کرنے کے لیے dump بنائیں اور اسے نئی data directory میں restore کریں۔

خود میزبانی والے AFFiNE کو کتنے CPU اور RAM درکار ہیں

AFFiNE کا requirements page کم از کم 4 CPU cores اور 2 GB RAM کا تقاضا کرتا ہے، اور جب آپ کی دستاویزات 10,000 الفاظ سے تجاوز کرتی ہیں تو مطلوبہ memory کو 4 GB تک بڑھا دیتا ہے۔ اسی page پر یہ بھی بتایا گیا ہے کہ memory کہاں استعمال ہوتی ہے: sync system اور document merging میں۔ اس میں ایک اہم عدد دیا گیا ہے جسے یاد رکھنا چاہیے: 10,000 modifications والی دستاویز کو merge کرتے وقت memory استعمال 1 GB تک پہنچ سکتی ہے۔

اب اس کا موازنہ ایسے 2 GB plan سے کریں جس پر دو افراد لکھ رہے ہوں۔ اوسط استعمال میں مسئلہ نہیں ہوتا۔ Postgres اور Node process حد سے کم memory استعمال کرتے ہیں اور کچھ memory باقی رہتی ہے۔ مسئلہ peak استعمال ہے۔ ایک بڑا merge پہلے سے استعمال ہونے والی memory کے علاوہ مزید 1 GB طلب کر سکتا ہے۔ 2 GB کے ایسے box پر جس میں swap نہ ہو، kernel کا out-of-memory (OOM) killer اس درخواست کا جواب سب سے بڑے process کو ختم کر کے دیتا ہے، اور وہ AFFiNE server ہوتا ہے۔

آپ کے ساتھی کو کوئی error دکھائی نہیں دیتا۔ انہیں صرف page reload ہوتا نظر آتا ہے، کیونکہ restart: unless-stopped چند seconds میں container کو دوبارہ چلا دیتا ہے۔ اس بارے میں اندازہ نہ لگائیں، تصدیق کریں:

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

پہلی command سے true، یا دوسری command سے node کا نام دینے والی Killed process line، اس بات کی نشاندہی کرتی ہے کہ memory ختم ہو گئی تھی، نہ کہ کوئی bug ملا ہے۔ دونوں سمتوں سے مسئلہ حل کریں۔ پہلے swap شامل کریں، تاکہ 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

اب free -h کو 2.0Gi کا swap total دکھانا چاہیے۔ Swap AFFiNE کو تیز نہیں بناتا، اور اس کا مقصد بھی یہ نہیں ہے۔ یہ ایک second کے spike کو container بند ہونے کے بجائے سست operation میں بدل دیتا ہے۔ حل کا دوسرا حصہ یہ ہے کہ Postgres کو اس cache کو اتنا بڑھانے سے روکیں جتنی جگہ application کو merge کے وقت درکار ہوتی ہے۔ اسی مقصد کے لیے Compose service پر memory limits استعمال ہوتی ہیں۔

Storage کا اندازہ لگانا کہیں آسان ہے۔ AFFiNE اسی 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
  }
]

Server install کے لیے 1.5 GB درکار ہیں۔ تقریباً 1,000 الفاظ پر مشتمل 1,000 دستاویزات Postgres data میں 0.1 GB شامل کرتی ہیں، جو تقریباً نہ ہونے کے برابر ہے۔ 1,000 uploaded files 10 GB شامل کرتی ہیں، اور اصل storage ضرورت یہی ہے۔ یہ اعداد running instance کی measurements نہیں بلکہ planning کے لیے شائع کیے گئے figures ہیں، اس لیے انہیں حتمی وعدہ نہیں بلکہ عمومی اندازہ سمجھیں۔ اہم بات یہ ہے: database چھوٹا رہتا ہے، جبکہ disk کی ضرورت آپ کی uploads طے کرتی ہیں۔

خود compose فائل لکھیں اور tags کو pin کریں

دستاویزی طریقہ curl -L -o docker-compose.yml https://github.com/toeverything/AFFiNE/releases/latest/download/docker-compose.yml کے ذریعے تیار شدہ فائل download کرتا ہے۔ یہ طریقہ کام کرتا ہے۔ لیکن اس پر انحصار کرنے سے پہلے ایک بات جاننا مفید ہے: 8 August 2026 تک release 0.27.3 کے ساتھ منسلک فائل اب بھی اپنے paths ایک .env فائل سے پڑھتی ہے اور ${UPLOAD_LOCATION}، ${CONFIG_LOCATION} اور ${DB_DATA_LOCATION} استعمال کرتی ہے، جبکہ documentation کا reference page نیا layout دکھاتا ہے جس میں سب کچھ ./data کے تحت رکھا گیا ہے اور .env کی بالکل ضرورت نہیں ہوتی۔ دونوں درست ہیں۔ فائل خود لکھنے سے یہ سوال ختم ہو جاتا ہے، اور images کو pin کرنے اور database password مقرر کرنے کے لیے آپ کو اسے بہرحال edit کرنا ہوگا۔

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

Compose project directory سے .env خود پڑھ لیتا ہے اور آپ کے لیے ${DB_PASSWORD} substitute کر دیتا ہے، اس لیے password اس فائل میں ظاہر نہیں ہوتا جسے آپ support thread میں paste کریں گے۔ آپ کے چلائے جانے والے ہر stack میں یہ طریقہ برقرار رکھنا مفید ہے، اور اس کی وجہ compose فائل سے secrets باہر رکھنے میں بیان کی گئی ہے۔

اب ~/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

upstream کی فراہم کردہ فائل سے چار فرق ہیں، اور ہر فرق کی ایک وجہ ہے۔

  • 127.0.0.1:3010:3010 port کو صرف loopback address پر publish کرتا ہے، اس لیے جب تک آپ طریقہ طے نہ کریں، server کے باہر سے کوئی بھی AFFiNE تک نہیں پہنچ سکتا۔ upstream کا '3010:3010' ہر interface پر bind ہوتا ہے، اور زیادہ تر VPS images میں اس میں public interface بھی شامل ہوتا ہے۔
  • POSTGRES_HOST_AUTH_METHOD: trust ہٹا دیا گیا ہے اور اس کے بجائے password مقرر کیا گیا ہے۔ Trust authentication اس database سے ہونے والی ہر connection کو affine user کے طور پر بغیر password کے قبول کرتی ہے۔ یہ private Compose network تک محدود ہے، جو اس وقت تک مناسب ہے جب تک آپ اس network سے ایک اور container نہ جوڑیں یا debugging کے دوران 5432 publish نہ کریں۔
  • redis:8-alpine ایک bare redis کی جگہ لیتا ہے، جو latest پر resolve ہوتا ہے۔ August 2026 تک یہ Redis 8 ہے، اس لیے pin اس major version کو برقرار رکھتا ہے جسے آپ نے test کیا ہے اور کسی غیر متعلقہ docker compose pull کے دوران مستقبل کا Redis 9 آنے سے روکتا ہے۔
  • pgvector/pgvector:pg16 upstream کی مقررہ قدر کے عین مطابق رہتا ہے، اوپر دی گئی وجہ کی بنا پر۔

POSTGRES_PASSWORD صرف اس وقت پڑھا جاتا ہے جب Postgres پہلی بار اپنی data directory بناتا ہے۔ جو instance پہلے سے موجود ہو، اس میں docker compose exec postgres psql -U affine -c "ALTER USER affine WITH PASSWORD 'yourpassword'" کے ذریعے password مقرر کریں اور پھر DATABASE_URL کو اسی کے مطابق update کریں۔

Configuration config/config.json میں رہتی ہے

AFFiNE اپنی ترتیبات config/config.json سے پڑھتا ہے، جو وہ ڈائریکٹری ہے جسے آپ نے /root/.affine/config پر mount کیا ہے۔ یہ فائل خودکار طور پر نہیں بنتی، اس لیے پہلی بار start کرنے سے پہلے اسے خود بنائیں۔ ایڈیٹر میں ~/affine/config/config.json کھولیں اور اس میں یہ مواد درج کریں۔ مثال میں موجود domain کی جگہ اپنا domain لکھیں:

{
  "$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 وہ address ہونا چاہیے جسے آپ کے صارفین browser میں کھولتے ہیں۔ AFFiNE اسی value سے share links اور workspace invitations بناتا ہے۔ اگر یہ http://localhost:3010 پر رہ جائے تو آپ کی بھیجی ہوئی invitation وصول کنندہ کی اپنی machine کی طرف اشارہ کرے گی اور وہاں ناکام ہو جائے گی۔ پہلی بار start کرنے سے پہلے اسے public HTTPS address پر set کریں، تاکہ فائل اور admin panel میں اس بارے میں اختلاف نہ ہو۔

copilot AI features کو control کرتا ہے۔ copilot.byok.enabled اپنی key استعمال کرنے کا اختیار ہے۔ اس سے workspace owner کو workspace settings میں اپنے model provider کی key paste کرنے کی اجازت ملتی ہے۔ AFFiNE کو self-host کرنے سے AI subscription شامل نہیں ہوتی۔ اگر آپ کو یہ اختیار نہیں چاہیے تو دونوں false رہنے دیں۔

Stack start کریں:

docker compose up -d
docker compose ps

docker compose ps میں affine_postgres اور affine_redis کی حالت healthy، affine_server کی حالت running، اور affine_migration_job کی state exited (0) ہونی چاہیے۔ migration job کا کوئی بھی دوسرا exit code وہ مسئلہ ہے جس کی تفتیش ضروری ہے۔ اس کا log بتاتا ہے کہ کون سا step رک گیا:

docker compose logs affine_migration

یاد آنے سے پہلے image کو pin کریں

stable ایک متحرک tag ہے۔ AFFiNE کا release workflow ہر stable build کے لیے کئی tags مقرر کرتا ہے، اور یہاں ان میں سے دو اہم ہیں: stable، جسے ہر release پر دوبارہ مقرر کیا جاتا ہے، اور stable- جس کے بعد git short hash ہوتا ہے، اور اسے تبدیل نہیں کیا جاتا۔ اگر stable برقرار رہے تو چھ ماہ بعد چلنے والا docker compose pull مختلف image حاصل کرے گا اور آپ کے database پر migrations ایسے وقت میں چلائے گا جس کا آپ نے انتخاب نہیں کیا تھا۔ آپ نے جس exact image کی جانچ کی ہے اسے pin کریں:

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

اس سے ghcr.io/toeverything/affine@sha256: کے بعد ایک طویل hash والی سطر ظاہر ہوتی ہے۔ پوری string کو دونوں affine اور affine_migration کی image: سطر میں paste کریں۔ ان دونوں کی values ہمیشہ یکساں ہونی چاہییں، کیونکہ یہ ایک ہی image کو دو کرداروں میں استعمال کرتے ہیں۔ عدم مطابقت کا مطلب ہے کہ database کو ایک schema میں migrate کیا جا رہا ہے، جبکہ اسے دوسرے schema کے ساتھ serve کیا جا رہا ہے۔ اس کے بعد upgrade ایک سوچی سمجھی ترمیم بن جاتا ہے، اچانک پیش آنے والا واقعہ نہیں: digest تبدیل کریں، backup لیں، docker compose pull، docker compose up -d۔

کسی اور سے پہلے admin اکاؤنٹ بنائیں

نئی instance پر /admin کھولنے پر AFFiNE آپ کو اکاؤنٹ بنانے والے صفحے پر بھیجتا ہے، کیونکہ سرور پر ابھی کوئی administrator موجود نہیں ہوتا۔ اس طریقۂ کار میں invitation code یا setup token نہیں ہوتا۔ اس صفحے کو سب سے پہلے کھولنے والا شخص آپ کے سرور کا administrator بن جاتا ہے، اس لیے رجسٹریشن مکمل ہونے تک port بند رہنا چاہیے۔

اسی وجہ سے اوپر دی گئی compose file 127.0.0.1 سے bind ہوتی ہے۔ اپنے کمپیوٹر سے SSH tunnel کے ذریعے اس تک رسائی حاصل کریں:

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

اس tunnel کو چلتا رہنے دیں اور اپنے مقامی browser میں http://127.0.0.1:3010/admin کھولیں۔ Register کریں اور log in کریں، پھر tunnel بند کر دیں۔ اب instance کو public name پر دستیاب کرنا محفوظ ہے۔

AFFiNE آپ کا ڈیٹا کہاں رکھتا ہے

تین paths میں تمام ڈیٹا موجود ہوتا ہے، اور یہ تینوں آپ کی بنائی ہوئی directory کے اندر ہوتے ہیں۔

  • ./data/postgres Postgres کی data directory ہے: documents، users، workspaces اور permissions۔
  • ./data/storage کو container میں /root/.affine/storage پر mount کیا جاتا ہے، اور اس میں upload کی گئی تمام files ہوتی ہیں۔
  • ./config کو /root/.affine/config پر mount کیا جاتا ہے، اور اس میں config.json ہوتا ہے۔

Upstream یہاں named volumes کے بجائے bind mounts استعمال کرتا ہے، اور یہ انتخاب دانستہ ہے: آپ عام commands سے ان paths کو tar اور copy کر سکتے ہیں، بغیر یہ پوچھے کہ Docker نے انہیں کہاں رکھا ہے۔ اس کا نقصان یہ ہے کہ host پر file ownership اب آپ کی ذمہ داری ہے۔ یہی وہ trade-off ہے جس کا احاطہ bind mounts اور named volumes میں کیا گیا ہے۔

AFFiNE کا بیک اپ کیسے لیں

بیک اپ لینے کے لیے دو چیزیں ہیں، اور دونوں کا طریقہ مختلف ہے۔ database ایک live server ہے، اس لیے اس کے چلتے ہوئے فائلیں copy کرنے سے copy corrupt ہو جاتی ہے۔ اس کے بجائے 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 container کے اندر اس کے local socket کے ذریعے چلتا ہے، اس لیے یہ password نہیں مانگتا۔ ls کے output میں اس کا size دیکھیں۔ چند سو bytes کی فائل کا مطلب ہے کہ dump ناکام ہو گیا، لیکن shell نے فائل پھر بھی بنا دی۔ اسی failure کا لوگوں کو چھ ماہ بعد پتا چلتا ہے۔ -T بھی اہم ہے: اس کے بغیر Compose terminal allocate کر سکتا ہے اور binary stream کو corrupt کر سکتا ہے۔

Uploaded files صرف files ہیں، اس لیے ان کا tar archive بنائیں:

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

config.json کو اپنے بیک اپ میں دستی طور پر محفوظ رکھیں۔ August 2026 میں جانچ کے مطابق AFFiNE documentation اب بھی admin panel سے configuration export کو not implemented بتاتی ہے، اس لیے disk پر موجود فائل ہی آپ کی settings کی واحد copy ہے۔ تینوں files کو server سے باہر copy کریں۔ جس disk پر محفوظ کی جانے والی چیز موجود ہو، اسی disk پر رکھا گیا بیک اپ بیک اپ نہیں ہوتا۔

بحالی، اور شائع شدہ مراحل میں ایک مسئلہ

ضرورت پڑنے سے پہلے سرکاری بحالی کے مراحل پڑھیں، اور انہیں غور سے پڑھیں۔ اگست 2026 میں شائع شدہ ہدایات کے مطابق affine.backup نام کی فائل container میں copy کی جاتی ہے، پھر ./pg.backup سے بحالی کی جاتی ہے۔ یہ دونوں مختلف نام ہیں۔ ان ہدایات میں ./postgres directory بھی حذف کی جاتی ہے، جبکہ موجودہ compose file اپنا data ./data/postgres میں رکھتی ہے۔ snippet میں دیے گئے paths کے بجائے وہ paths استعمال کریں جو آپ نے حقیقت میں استعمال کیے ہیں۔ اس guide کے layout کے مطابق sequence یہ ہے:

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

rm کے بجائے mv کا استعمال نوٹ کریں۔ جس database کی copy محفوظ نہ رکھی گئی ہو، اس پر بحالی کرنا ایک غلط command کو مکمل data loss میں تبدیل کر سکتا ہے۔ پرانی directory کو عارضی طور پر الگ جگہ منتقل کرنے میں کچھ خرچ نہیں ہوتا۔ uploads بھی tar xzf backup/storage-2026-08-08.tgz -C data کے ذریعے restore کریں، ورنہ ہر document میں attachments خراب دکھائی دیں گی۔ اس کے بعد log in کریں اور ایسا document کھولیں جس میں image موجود ہو۔ یہی test ہے۔ جس restore کو آپ نے browser میں کھول کر verify نہیں کیا، وہ backup نہیں بلکہ صرف ایک file ہے۔

پہلے سے چلنے والے proxy کے پیچھے AFFiNE رکھنا

AFFiNE WebSocket استعمال کرتا ہے، اور یہ اختیاری نہیں ہے۔ دستاویزات میں اس بارے میں واضح ہدایت ہے: WebSocket، AFFiNE کے sync اور collaboration system کی بنیاد ہے۔ اس لیے جو proxy ان connections کو upgrade نہ کرے، اس میں workspace کی editing خاموشی سے sync ہونا بند ہو جاتی ہے۔ page کھلتا ہے، login کام کرتا ہے، لیکن ایک browser میں کی گئی edit دوسرے browser تک نہیں پہنچتی۔ اپنے browser کے developer tools میں Network tab کھولیں اور WS پر filter کریں۔ جو connection بار بار کھلے اور بند ہو، وہ اس proxy کی نشانی ہے جو upgrade کو آگے منتقل نہیں کر رہا۔

اگر آپ دوسرے containers کے لیے پہلے سے Traefik چلا رہے ہیں تو AFFiNE اس میں ایک عام service کے طور پر شامل ہو جاتا ہے۔ affine service سے ports: block حذف کریں، پھر یہ شامل کریں:

    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'

اور file کے آخر میں، services: کے ساتھ:

networks:
  proxy:
    external: true

Certificate resolver کا نام وہی ہونا چاہیے جو آپ کی Traefik configuration میں defined ہے، اور loadbalancer.server.port container port 3010 ہے، host port نہیں۔ Traefik WebSocket connections کو اضافی configuration کے بغیر proxy کرتا ہے، اس لیے مزید کچھ شامل کرنے کی ضرورت نہیں۔ اگر آپ کے stack کی باقی apps پہلے ہی single sign-on کے لیے Authentik کے پیچھے ہیں تو اس router پر forward auth middleware کے ذریعے AFFiNE تک browser access محدود کیا جا سکتا ہے، لیکن desktop app کی testing مکمل ہونے تک اسے فعال نہ کریں۔ Desktop app کے پاس browser session نہیں ہوتا اور وہ sync کرنے میں ناکام ہو جائے گی۔ ایک ہی instance کے پیچھے متعدد apps چلانے کا طریقہ متعدد apps کے سامنے ایک 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 کی default قدر 1 MB ہے۔ اس line کے بغیر چھوٹی photo سے بڑی ہر upload 413 status کے ساتھ ناکام ہو جاتی ہے، اور AFFiNE logs میں کچھ ظاہر نہیں ہوتا، کیونکہ request وہاں تک پہنچی ہی نہیں۔ Caddy کے لیے صرف ایک line، reverse_proxy http://127.0.0.1:3010، درکار ہے؛ یہ certificates اور WebSocket upgrades خود manage کرتا ہے۔

Self-hosted build میں شامل نہ ہونے والی چیزیں

ٹیم منتقل کرنے سے پہلے اس حقیقت کے بارے میں خود سے دیانت دار رہیں۔

Real-time collaboration موجود ہے، اور sizing سے متعلق تمام مشورے اسی feature پر مبنی ہیں، کیونکہ AFFiNE کی اپنی documentation کے مطابق memory استعمال sync system اور document merging کی وجہ سے ہوتا ہے۔ Offline editing وہ وجہ ہے جس کی بنا پر بہت سے لوگ local-first tool چاہتے ہیں، اور desktop application آپ کے self-hosted server کو اپنی workspace list میں شامل کر کے اس کے خلاف login کر سکتی ہے۔ عہد کرنے سے پہلے اپنی ٹیم کے لیے ضروری exact offline behaviour کی جانچ کریں: network بند کر کے desktop app میں edit کریں، دوبارہ connect کریں، پھر دوسرے device پر نتیجہ دیکھیں۔ Feature lists ثبوت نہیں ہوتیں، اور اس فہرست کا بھی یہی معاملہ ہے۔

Shipped compose file میں server-side full-text search بند ہے، جہاں AFFINE_INDEXER_ENABLED=false کو server اور migration job دونوں پر set کیا گیا ہے۔ اسے فعال کرنے کے لیے Manticore Search container شامل کرنا ہوگا۔ اس سے یہ پانچویں service بن جاتی ہے اور memory استعمال بڑھ جاتا ہے۔ 2 GB کے server پر یہی تبدیلی حد سے تجاوز کا سبب بنتی ہے۔ Client کے اندر search آپ کی کھلی ہوئی workspace میں پھر بھی کام کرتی ہے۔

لوگوں کو شامل کرنے سے پہلے دو limits جاننا ضروری ہیں۔ Self-hosted workspace میں زیادہ سے زیادہ 10 seats دی جاتی ہیں، اور اس حد سے تجاوز کے لیے AFFiNE کا Team license درکار ہوتا ہے۔ Self-hosted instances کے لیے unlimited blob storage اور unlimited blob size کو documentation میں مطلوبہ feature کے طور پر بیان کیا گیا ہے، لیکن August 2026 میں یہ ابھی مکمل طور پر implemented نہیں تھے۔ گھرانے یا چھوٹی ٹیم کے لیے ان میں سے کوئی بات اہم نہیں۔ اگر آپ 40 لوگوں کو منتقل کرنے کا منصوبہ بنا رہے تھے تو دونوں باتیں اہم ہیں۔

اپ گریڈز

سب سے پہلے release notes پڑھیں، خاص طور پر 0.26 سے 0.27 جیسی minor version bump کے لیے، کیونکہ breaking changes اسی مرحلے میں شامل ہو سکتی ہیں۔ کسی بھی تبدیلی سے پہلے database اور storage directory کا backup لیں، کیونکہ اگلی start پر migration job schema میں تبدیلی کرتی ہے اور اسے واپس کرنے کا کوئی طریقہ نہیں ہوتا۔ اس کے بعد pinned digest تبدیل کریں، docker compose pull چلائیں، پھر docker compose up -d چلائیں، اور docker compose logs -f affine_migration کو monitor کریں جب تک یہ صاف طور پر exit نہ ہو جائے۔ اس کے بعد docker image prune پرانی layers صاف کر دیتا ہے۔ بہت پرانی installation استعمال کرنے والوں کے لیے ایک تاریخی نوٹ: version 0.23.0 سے image name affine-graphql سے بدل کر affine ہو گیا تھا، اس لیے اس سے پرانی compose file میں image lines دوبارہ لکھنی ہوں گی، ورنہ pull کو مطلوبہ image نہیں ملے گی۔

FAQ

AFFiNE container کبھی start کیوں نہیں ہوتا؟

affine service، affine_migration job پر condition: service_completed_successfully کا اعلان کرتی ہے۔ اس لیے اگر migration، 0 کے علاوہ کسی بھی status کے ساتھ ختم ہو، تو server start نہیں ہوتا اور web interface بالکل ظاہر نہیں ہوتا۔ یہ دیکھنے کے لیے docker compose logs affine_migration چلائیں کہ کون سا step رک گیا۔ ہاتھ سے edit کی گئی compose file میں عام وجہ pgvector/pgvector:pg16 کے بجائے stock postgres image کا استعمال ہے، کیونکہ AFFiNE schema، pgvector extension کا اعلان کرتی ہے اور vector(1024) columns والی tables بناتی ہے، جو plain Postgres نہیں بنا سکتا۔

self-hosted AFFiNE کو کتنی RAM درکار ہے؟

AFFiNE کے requirements page کے مطابق کم از کم 4 CPU cores اور 2 GB RAM درکار ہے۔ Documents میں 10,000 words سے زیادہ ہونے پر ضرورت 4 GB تک بڑھ جاتی ہے۔ اس page میں یہ بھی بتایا گیا ہے کہ 10,000 modifications والی document merge کرتے وقت memory استعمال 1 GB تک پہنچ سکتی ہے۔ 2 GB server پر مسئلہ idle load نہیں بلکہ یہی peak ہے۔ kernel کا out-of-memory killer AFFiNE process کو روک دیتا ہے، اور restart: unless-stopped اسے دوبارہ start کرتا ہے۔ اس لیے users کو error کے بجائے page reload دکھائی دیتا ہے۔ docker inspect affine_server --format '{{.State.OOMKilled}}' اور sudo dmesg -T | grep -i 'out of memory' سے اس کی تصدیق کریں، پھر 2 GB کی swap file شامل کریں تاکہ memory spike سست ہو، fatal نہ ہو۔

AFFiNE میرا data کہاں محفوظ کرتا ہے، اور مجھے کیا backup کرنا چاہیے؟

آپ کی compose directory کے اندر تین paths ہر چیز محفوظ رکھتے ہیں: database کے لیے ./data/postgres، uploaded files کے لیے ./data/storage، اور config.json کے لیے ./config۔ Database کا backup docker compose exec -T postgres pg_dump --format c --username affine affine > affine.dump سے لیں، files copy کر کے نہیں، کیونکہ running Postgres کو محفوظ طریقے سے copy نہیں کیا جا سکتا۔ Uploads کے لیے ./data/storage کو tar کریں، اور config.json کی ایک copy دستی طور پر محفوظ رکھیں، کیونکہ August 2026 تک admin panel سے configuration export کو ابھی implemented نہیں بتایا گیا ہے۔

کیا self-hosted AFFiNE پر real-time collaboration کام کرتی ہے؟

ہاں، اور اسے فعال کرنے کے لیے کسی اضافی setting کی ضرورت نہیں۔ واحد شرط آپ کا reverse proxy ہے، کیونکہ sync، WebSocket connections کے ذریعے چلتی ہے۔ nginx پر اس کا مطلب proxy_http_version 1.1 کے ساتھ Upgrade اور Connection: upgrade headers ہیں، جبکہ Traefik اور Caddy ان connections کو اضافی configuration کے بغیر آگے منتقل کر دیتے ہیں۔ اگر proxy ان connections کو upgrade نہ کرے تو workspace معمول کے مطابق load اور login ہوتا ہے، لیکن ایک browser میں کی گئی edits دوسرے browser میں ظاہر نہیں ہوتیں۔

کیا میں AFFiNE کو stock Postgres image کے ساتھ چلا سکتا ہوں؟

نہیں۔ AFFiNE کا schema.prisma، extensions = [pgvector(map: "vector")] کا اعلان کرتا ہے اور vector(1024) type کے embedding column والی چار tables define کرتا ہے۔ Migration job یہ tables اس وقت بھی بناتی ہے جب AI features بند ہوں۔ pgvector/pgvector:pg16 استعمال کریں، جو Postgres 16 ہے اور اس extension کو پہلے سے compile کیے ہوئے ہے۔ اگر اس کے بجائے AFFiNE کو external Postgres server سے connect کریں تو اس server پر pgvector install کریں اور migration چلانے سے پہلے target database میں extension create کریں۔