Affine
Affine is an open-source knowledge management platform, similar to Notion. You can organise and collaborate on documents and wikis, with support for markdown, rich embeds, and full-text search.
This compose file deploys Affine with a pgvector database, Valkey for caching, and Manticore Search for full-text indexing. A migration job runs automatically before the main service starts. Volumes are stored on NFS, and the Affine UI is proxied through a Traefik reverse proxy with Cloudflare TLS certificates.
Docker Compose
# compose.yaml
services:
affine:
image: ghcr.io/toeverything/affine:stable
container_name: affine-server
restart: unless-stopped
depends_on:
affine_valkey:
condition: service_healthy
affine_postgres:
condition: service_healthy
affine_migration:
condition: service_completed_successfully
affine_manticore:
condition: service_healthy
# ports:
# - "3010:3010"
networks:
- affine
- affine_proxy
volumes:
- type: volume
source: docker-nfs
target: /root/.affine/storage
volume:
subpath: affine/upload
- type: volume
source: docker-nfs
target: /root/.affine/config
volume:
subpath: affine/config
environment:
REDIS_SERVER_HOST: affine_valkey
REDIS_SERVER_PASSWORD: ${VALKEY_PASSWORD}
DATABASE_URL: postgresql://${DB_USERNAME}:${DB_PASSWORD}@affine_postgres:5432/${DB_DATABASE}
AFFINE_INDEXER_ENABLED: true
AFFINE_INDEXER_SEARCH_ENDPOINT: http://affine_manticore:9308
TZ: Europe/London
labels:
- "traefik.enable=true"
- "traefik.docker.network=affine_proxy"
- "traefik.http.services.affine.loadbalancer.server.port=3010"
- "traefik.http.routers.affine.rule=Host(`affine.${TRAEFIK_BASE_URL}`)"
- "traefik.http.routers.affine.entrypoints=websecure"
- "traefik.http.routers.affine.tls.certresolver=cloudflare"
affine_migration:
image: ghcr.io/toeverything/affine:stable
container_name: affine-migration-job
restart: no
depends_on:
affine_postgres:
condition: service_healthy
affine_valkey:
condition: service_healthy
networks:
- affine
volumes:
- type: volume
source: docker-nfs
target: /root/.affine/storage
volume:
subpath: affine/upload
- type: volume
source: docker-nfs
target: /root/.affine/config
volume:
subpath: affine/config
command: ["sh", "-c", "node ./scripts/self-host-predeploy.js"]
environment:
REDIS_SERVER_HOST: affine_valkey
REDIS_SERVER_PASSWORD: ${VALKEY_PASSWORD}
DATABASE_URL: postgresql://${DB_USERNAME}:${DB_PASSWORD}@affine_postgres:5432/${DB_DATABASE}
AFFINE_INDEXER_ENABLED: true
AFFINE_INDEXER_SEARCH_ENDPOINT: http://affine_manticore:9308
TZ: Europe/London
affine_manticore:
image: manticoresearch/manticore:14.1.0
container_name: affine_manticore
restart: unless-stopped
networks:
- affine
volumes:
- type: volume
source: docker-nfs
target: /var/lib/manticore
volume:
subpath: affine/manticore
ulimits:
nproc: 65535
nofile:
soft: 65535
hard: 65535
memlock:
soft: -1
hard: -1
healthcheck:
test: ["CMD", "wget", "-O-", "http://127.0.0.1:9308"]
interval: 10s
timeout: 5s
retries: 5
environment:
TZ: Europe/London
affine_valkey:
image: valkey/valkey:9
container_name: affine_valkey
restart: unless-stopped
networks:
- affine
volumes:
- type: volume
source: docker-nfs
target: /data
volume:
subpath: affine/valkey
healthcheck:
test: ["CMD", "valkey-cli", "--raw", "incr", "ping"]
interval: 10s
timeout: 5s
retries: 5
command: valkey-server --save 30 1 --loglevel warning --requirepass ${VALKEY_PASSWORD}
environment:
TZ: Europe/London
affine_postgres:
image: pgvector/pgvector:pg18-trixie
container_name: affine_postgres
restart: unless-stopped
networks:
- affine
volumes:
- type: volume
source: docker-nfs
target: /var/lib/postgresql
volume:
subpath: affine/postgres
healthcheck:
test:
["CMD", "pg_isready", "-U", "${DB_USERNAME}", "-d", "${DB_DATABASE}"]
interval: 10s
timeout: 5s
retries: 5
environment:
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_DATABASE}
POSTGRES_INITDB_ARGS: "--data-checksums"
TZ: Europe/London
volumes:
docker-nfs:
driver: local
driver_opts:
type: nfs
o: addr=xxx.xxx.xxx.xxx,nolock,soft,rw,nfsvers=4.2
device: :/mnt/nfs-volume
networks:
affine:
name: affine
affine_proxy:
name: affine_proxy
Environment Variables
# .env
AFFINE_REVISION=stable
TRAEFIK_BASE_URL=example.com
AFFINE_SERVER_EXTERNAL_URL=https://affine.${TRAEFIK_BASE_URL}
DB_USERNAME=
DB_PASSWORD=
DB_DATABASE=
VALKEY_PASSWORD=
Traefik Configuration
# compose.yaml (excerpt)
services:
traefik:
image: traefik:latest
container_name: traefik
...
networks:
- traefik
# here
- affine_proxy
...
networks:
# here
affine_proxy:
name: affine_proxy