Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung Docker 22.08.2026 · 10 min Lesezeit

AFFiNE mit Docker installieren: All-in-one Notion-Alternative mit Whiteboard und Kanban selbst hosten

AFFiNE vereint Docs, Whiteboard und Kanban in einer einzigen Plattform – lokal deploybar mit voller Datenkontrolle. Diese Anleitung zeigt dir Schritt für Schritt, wie du AFFiNE per Docker Compose mit PostgreSQL und Redis aufsetzte, absicherst und aktuell hältst.

AFFiNE mit Docker installieren und als selbst gehostete Open Source Notion Alternative mit integriertem Whiteboard, Kanban Boards und All in one Workspace betreiben. KI-generiert

Wer Notion, Miro und Trello in einem einzigen selbst gehosteten Tool zusammenführen will, landet früher oder später bei AFFiNE. Das quelloffene Projekt vereint strukturierte Docs und Datenbanken, ein freies Whiteboard/Canvas und Kanban-Boards in einer einzigen Oberfläche – und lässt sich dank einer local-first-Architektur vollständig auf dem eigenen Server betreiben. Mit über 69.000 GitHub-Stars ist AFFiNE die bekannteste visuelle Notion-Alternative für datenbewusste Teams und KMU, die keine Abhängigkeit von einem Cloud-Anbieter eingehen wollen. Diese Anleitung richtet sich an IT-Admins und ambitionierte Selfhoster, die AFFiNE auf einem beliebigen Linux-Host mit Docker Compose aufsetzen möchten.

Voraussetzungen

  1. Docker Engine >= 20.10 und Docker Compose Plugin v2 (oder Docker Desktop) – noch nicht installiert? Unsere Grundlagenanleitung Docker und Docker Compose auf Linux installieren führt dich durch den Setup.
  2. Linux-Host, VM oder NAS mit mindestens 4 CPU-Kernen und 2 GB RAM; weniger führt zu Latenzproblemen bei mehreren gleichzeitigen Nutzern.
  3. Ausreichend Festplattenplatz für PostgreSQL-Daten, Uploads und Konfiguration (je nach Nutzung mehrere GB).
  4. openssl zum Erzeugen eines sicheren Datenbankpassworts (auf jedem Linux-System vorhanden).
  5. Für den Produktionsbetrieb: öffentliche IP-Adresse, Domain und ein Reverse-Proxy mit SSL-Terminierung (z. B. Traefik als Docker-Reverse-Proxy oder Caddy).
  6. Optional: SMTP-Zugangsdaten für E-Mail-Einladungen und Benachrichtigungen.

Schritt 1: Projektordner anlegen und Verzeichnisstruktur erstellen

Alle Dateien für den AFFiNE-Stack leben in einem gemeinsamen Projektordner. Lege ihn an einem Ort an, der genug Platz hat und den du in deine Backup-Routine einbeziehen kannst – zum Beispiel unter /opt/affine/.

mkdir -p /opt/affine
cd /opt/affine
mkdir -p postgres storage config

Die drei Unterverzeichnisse entsprechen den Pflicht-Volumes: postgres/ für die Datenbankdaten, storage/ für Uploads und Blobs, config/ für Konfigurationsdateien. Du kannst die Pfade später in der .env anpassen, solltest sie danach aber niemals mehr ändern, ohne vorher einen vollständigen Backup-und-Restore-Workflow durchzuführen.

Verifizieren: ls -la /opt/affine/ sollte die drei Unterverzeichnisse postgres, storage und config anzeigen.

Schritt 2: .env-Datei mit Secrets und Pfaden anlegen

Die .env-Datei enthält alle sensiblen und umgebungsspezifischen Werte. Das Datenbankpasswort muss zwingend vor dem ersten Start gesetzt sein – nachträgliche Änderungen erfordern einen vollständigen Datenbankdump und eine Neuinitialisierung.

Generiere zunächst ein starkes Passwort:

openssl rand -base64 32

Erstelle dann die .env im Projektordner und ersetze DEIN_STARKES_PASSWORT durch die eben generierte Zeichenkette:

# ── AFFiNE Self-Host .env ──────────────────────────────────────────────────

# Datenbankzugang (PFLICHT vor erstem Start – danach nicht mehr ändern!)
DB_USERNAME=affine
DB_PASSWORD=DEIN_STARKES_PASSWORT
DB_DATABASE=affine

# Speicherpfade (absolute oder relative Host-Pfade)
DB_DATA_LOCATION=./postgres
UPLOAD_LOCATION=./storage
CONFIG_LOCATION=./config

# Image-Kanal: stable | beta | canary (für Produktion: stable)
AFFINE_REVISION=stable

# Host-Port für die Web-Oberfläche (Standard: 3010)
PORT=3010

# ── Produktionsbetrieb (Domain / HTTPS) ───────────────────────────────────
# Auskommentiert lassen, solange du nur lokal testest.
# AFFINE_SERVER_HOST=affine.example.com
# AFFINE_SERVER_HTTPS=true
# AFFINE_SERVER_EXTERNAL_URL=https://affine.example.com

# ── E-Mail (optional, für Einladungen) ────────────────────────────────────
# MAILER_HOST=smtp.example.com
# MAILER_PORT=465
# MAILER_USER=noreply@example.com
# MAILER_PASSWORD=SMTP_PASSWORT
# MAILER_SENDER=noreply@example.com

Sichere die Datei sofort gegen unberechtigten Zugriff:

chmod 600 /opt/affine/.env

Verifizieren: grep DB_PASSWORD /opt/affine/.env muss dein gesetztes Passwort zurückgeben (kein leerer Wert). ls -la /opt/affine/.env sollte Berechtigungen -rw------- zeigen.

Schritt 3: compose.yaml anlegen

Der Stack besteht aus vier Services. Der affine_migration-Container führt Datenbankmigrationen aus, bevor der Haupt-Container startet – das ist ein häufig übersehenes Detail in Community-Anleitungen, das ohne den Migrations-Job zu Startfehlern führt. Verwende ausschließlich das aktuelle Image ghcr.io/toeverything/affine; das ältere affine-self-hosted-Image ist veraltet und nutzt noch Port 3000.

name: affine

services:
  affine:
    image: ghcr.io/toeverything/affine:${AFFINE_REVISION:-stable}
    container_name: affine_server
    ports:
      - '${PORT:-3010}:3010'
    depends_on:
      redis:
        condition: service_healthy
      postgres:
        condition: service_healthy
      affine_migration:
        condition: service_completed_successfully
    volumes:
      - ${UPLOAD_LOCATION}:/root/.affine/storage
      - ${CONFIG_LOCATION}:/root/.affine/config
    env_file:
      - .env
    environment:
      - REDIS_SERVER_HOST=redis
      - DATABASE_URL=postgresql://${DB_USERNAME}:${DB_PASSWORD}@postgres:5432/${DB_DATABASE:-affine}
      - AFFINE_INDEXER_ENABLED=false
    restart: unless-stopped

  affine_migration:
    image: ghcr.io/toeverything/affine:${AFFINE_REVISION:-stable}
    container_name: affine_migration_job
    volumes:
      - ${UPLOAD_LOCATION}:/root/.affine/storage
      - ${CONFIG_LOCATION}:/root/.affine/config
    command: ['sh', '-c', 'node ./scripts/self-host-predeploy.js']
    env_file:
      - .env
    environment:
      - REDIS_SERVER_HOST=redis
      - DATABASE_URL=postgresql://${DB_USERNAME}:${DB_PASSWORD}@postgres:5432/${DB_DATABASE:-affine}
      - AFFINE_INDEXER_ENABLED=false
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy

  redis:
    image: redis
    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:
      - ${DB_DATA_LOCATION}:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: ${DB_USERNAME}
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: ${DB_DATABASE:-affine}
      POSTGRES_INITDB_ARGS: '--data-checksums'
    healthcheck:
      test: ['CMD', 'pg_isready', '-U', "${DB_USERNAME}", '-d', "${DB_DATABASE:-affine}"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

Wichtig: PostgreSQL muss als pgvector/pgvector:pg16 laufen – nicht als Standard-postgres:16 – da AFFiNE die pgvector-Erweiterung für Vektorsuche und KI-Features benötigt.

Eckdaten auf einen Blick

ParameterWert
AFFiNE-Imageghcr.io/toeverything/affine:stable
PostgreSQL-Imagepgvector/pgvector:pg16
Redis-Imageredis (latest)
Web-UI Port3010 (konfigurierbar via PORT)
Upload-VolumeUPLOAD_LOCATION → /root/.affine/storage
Config-VolumeCONFIG_LOCATION → /root/.affine/config
DB-VolumeDB_DATA_LOCATION → /var/lib/postgresql/data
Mindest-Ressourcen4 CPU-Kerne, 2 GB RAM
LizenzMIT (Community Edition)

Verifizieren: docker compose config im Projektordner gibt die zusammengeführte Konfiguration ohne Fehler aus. Kein „variable is not set"-Warning sollte erscheinen.

Schritt 4: Stack starten

Starte alle Container im Hintergrund. Docker Compose lädt zunächst alle Images von der GitHub Container Registry herunter – je nach Verbindung dauert das einige Minuten.

cd /opt/affine
docker compose up -d

Der Startablauf ist durch die depends_on-Bedingungen streng geordnet: Zuerst starten Redis und PostgreSQL, deren Healthchecks müssen grün sein. Dann führt affine_migration die Datenbankmigrationen aus und muss erfolgreich abschließen (service_completed_successfully). Erst danach startet der eigentliche affine_server.

Verifizieren:

# Alle Container anzeigen
docker compose ps

# Erwartete Ausgabe (nach 1-2 Minuten):
# NAME                    STATUS
# affine_server           Up (running)
# affine_migration_job    Exited (0)
# affine_postgres         Up (healthy)
# affine_redis            Up (healthy)

# Logs der Haupt-App prüfen
docker compose logs affine --tail=30

# HTTP-Antwort prüfen
curl -I http://localhost:3010

Der affine_migration_job mit Exit-Code 0 ist korrekt – er ist ein einmaliger Job, kein dauerhaft laufender Dienst. curl -I http://localhost:3010 sollte HTTP/1.1 200 oder einen 302-Redirect zurückgeben. Falls affine_server nicht startet, liefert docker compose logs affine_migration den Grund.

Schritt 5: Ersteinrichtung im Browser

Rufe http://localhost:3010 (oder die IP deines Servers mit Port 3010) im Browser auf. AFFiNE zeigt beim ersten Aufruf einen Einrichtungsassistenten.

  1. Der erste registrierte Benutzer erhält automatisch Admin-Rechte – lege daher sofort nach dem ersten Start deinen Admin-Account an.
  2. Workspace-Name und Einstellungen kannst du im Assistenten frei wählen.
  3. E-Mail-Einladungen an weitere Teammitglieder funktionieren erst, wenn du die MAILER_*-Variablen in der .env gesetzt und den Stack neu gestartet hast (docker compose up -d).
  4. Für Einladungs- und Share-Links mit der richtigen externen URL musst du im Produktionsbetrieb AFFINE_SERVER_EXTERNAL_URL setzen (siehe Schritt 6).

Verifizieren: Nach dem Anlegen des Admin-Accounts öffnet sich das AFFiNE-Dashboard. Du siehst die drei Hauptmodi: Docs, Edgeless (Whiteboard) und Database (Kanban/Grid). Ein neues Dokument lässt sich anlegen und speichern, ohne Fehlermeldung.

Schritt 6: HTTPS und Produktionsbetrieb (optional, aber empfohlen)

AFFiNE selbst kennt kein eigenes HTTPS – die SSL-Terminierung übernimmt ein vorgelagerter Reverse-Proxy. Wie du Traefik mit automatischem Let's-Encrypt-Zertifikat einrichtest, beschreibt unsere Anleitung Traefik als Docker-Reverse-Proxy mit automatischem HTTPS. Für einen manuellen Nginx-Aufbau hilft Nginx als Reverse Proxy mit TLS manuell einrichten.

Sobald der Reverse-Proxy läuft, ergänze in der .env die folgenden Zeilen (Raute entfernen) und starte den Stack neu:

AFFINE_SERVER_HOST=affine.example.com
AFFINE_SERVER_HTTPS=true
AFFINE_SERVER_EXTERNAL_URL=https://affine.example.com
docker compose up -d

Ohne AFFINE_SERVER_EXTERNAL_URL erzeugt AFFiNE Einladungs- und Share-Links mit localhost:3010, die für externe Nutzer nicht erreichbar sind. Einige Features wie Passwortänderung per E-Mail erfordern außerdem zwingend eine HTTPS-Umgebung (WSS).

Verifizieren: Rufe https://affine.example.com im Browser auf. Das Schloss-Symbol im Browser zeigt ein gültiges Zertifikat. Erstelle eine Einladung für einen zweiten Benutzer und prüfe, ob der Link in der E-Mail https://affine.example.com/... enthält (nicht localhost).

Schritt 7: Updates und Backup-Routine

AFFiNE befindet sich noch im Pre-1.0-Stadium und wird aktiv weiterentwickelt. Neue Releases bringen automatisch Datenbankmigrationen mit – ein Rollback ist ohne vorheriges Backup nicht möglich.

Update-Workflow

cd /opt/affine

# 1. Backup ZUERST (vor jedem Update!)
docker compose exec postgres pg_dump -U affine affine > affine_backup_$(date +%Y%m%d).sql
cp -a ./storage ./storage_backup_$(date +%Y%m%d)

# 2. Neue Images ziehen
docker compose pull

# 3. Stack neu starten (affine_migration läuft automatisch)
docker compose up -d

# 4. Logs prüfen
docker compose logs affine --tail=20

Backup-Strategie

Drei Komponenten müssen regelmäßig gesichert werden:

  1. PostgreSQL-Datenbank: Via pg_dump (wie oben) oder als Volume-Backup von DB_DATA_LOCATION.
  2. Blob-Storage: Gesamter Inhalt von UPLOAD_LOCATION (hochgeladene Dateien, Bilder).
  3. Konfiguration: Inhalt von CONFIG_LOCATION sowie die .env-Datei.

Eine vollständige Backup-Automatisierung mit Rotation und Cloud-Sync beschreibt unsere Anleitung MySQL & PostgreSQL Backup automatisieren mit cron.

Verifizieren: Nach dem Update zeigt docker compose ps alle Services als Up bzw. den Migrations-Job als Exited (0). docker compose logs affine --tail=30 enthält keine Fehler zu Datenbankverbindung oder Migrationen. Die AFFiNE-Web-UI ist erreichbar und alle bestehenden Workspaces und Dokumente sind noch vorhanden.

Troubleshooting / Typische Fehler

  1. affine_server startet nicht / wartet ewig: Der Migrations-Job ist fehlgeschlagen. Diagnose: docker compose logs affine_migration. Häufigste Ursache: PostgreSQL war beim ersten Start noch nicht bereit. Lösung: docker compose down && docker compose up -d erneut ausführen. Die Healthchecks auf postgres und redis verhindern das normalerweise – prüfe, ob beide den Status healthy haben.
  2. Falsches Image / Port 3000 statt 3010: Du verwendest das veraltete ghcr.io/toeverything/affine-self-hosted:latest. Ersetze das Image in der compose.yaml durch ghcr.io/toeverything/affine:${AFFINE_REVISION:-stable} und führe docker compose up -d --force-recreate aus.
  3. DB_PASSWORD leer / Datenbankverbindung schlägt fehl: Wenn DB_PASSWORD in der .env fehlt oder leer ist, schlägt die PostgreSQL-Initialisierung fehl. Bei einer noch nicht initialisierten Datenbank: .env mit Passwort befüllen, docker compose down -v (Achtung: löscht Daten!), docker compose up -d. Bei bereits existierenden Daten ist ein vollständiger Dump, Neuinitialisierung und Restore nötig.
  4. Einladungslinks zeigen localhost:3010: AFFINE_SERVER_EXTERNAL_URL ist nicht gesetzt. In .env eintragen und docker compose up -d ausführen.
  5. Fehler „pgvector extension not found": Du verwendest postgres:16 statt pgvector/pgvector:pg16. Images tauschen, bestehende Daten per pg_dump sichern, DB-Volume löschen, mit korrektem Image neu starten und Daten einspielen.
  6. Langsame Performance bei mehreren Nutzern: Ressourcen unter dem Minimum (4 CPU, 2 GB RAM). Prüfe mit docker stats auf Memory-Druck oder CPU-Throttling.
  7. Redis-Reconnect-Fehler nach Neustart: Redis hat kein persistiertes Volume – das ist im offiziellen Setup so vorgesehen. Falls du Redis-Persistenz benötigst, füge dem Redis-Service ein benanntes Volume hinzu.

Häufige Fragen

Welchen Tag soll ich für AFFINE_REVISION verwenden?

Für Produktionsbetrieb immer stable (Standard). Dieser Tag zeigt stets auf die neueste stabile Version (aktuell v0.26.3). Mit beta erhältst du frühzeitig neue Features, mit canary die aktuellen Entwicklungs-Builds – Letzteres ist ausschließlich für Entwickler und Tester geeignet, da Breaking Changes möglich sind.

Wie aktualisiere ich AFFiNE auf eine neue Version?

Führe im Projektordner aus: docker compose pull && docker compose up -d. Der affine_migration-Container läuft automatisch mit und führt notwendige Datenbankmigrationen aus. Erstelle zwingend vorher ein Backup von PostgreSQL und dem UPLOAD_LOCATION-Verzeichnis.

Wie richte ich den ersten Admin-Account ein?

Nach dem ersten erfolgreichen Start rufst du http://localhost:3010 auf. AFFiNE zeigt einen Ersteinrichtungsassistenten. Der erste Benutzer, der sich registriert, erhält automatisch Admin-Rechte. Weitere Benutzer kannst du anschließend über das Admin-Panel einladen – vorausgesetzt, die MAILER-Variablen sind konfiguriert.

Kann ich AFFiNE auf ARM64 (Raspberry Pi, Apple Silicon) betreiben?

Die Basis-Images (redis, pgvector/pgvector:pg16) unterstützen arm64. Für das AFFiNE-Image selbst solltest du die ghcr.io-Registry auf Multi-Arch-Support prüfen. Für den Produktionseinsatz auf ARM64 empfiehlt sich ein ausgiebiger Vortest.

Wie sichere ich AFFiNE vollständig?

Drei Komponenten müssen gesichert werden: (1) PostgreSQL-Datenbank via pg_dump oder Volume-Backup von DB_DATA_LOCATION, (2) der gesamte Blob-Storage (UPLOAD_LOCATION), (3) die Konfiguration (CONFIG_LOCATION plus die .env-Datei). Teste die Wiederherstellung regelmäßig in einer separaten Umgebung – ein Backup, dessen Restore nicht geprüft wurde, ist kein echtes Backup.

Fazit

AFFiNE ist eine der überzeugendsten visuellen Notion-Alternativen für Teams, die ihre Daten selbst kontrollieren wollen. Der Setup per Docker Compose ist mit vier Containern etwas umfangreicher als bei simpleren Tools, aber gut durchdacht: Der Migrations-Container sorgt für saubere Datenbankinitialisierung, Healthchecks auf PostgreSQL und Redis verhindern Race-Conditions beim Start. Die wichtigsten Fallstricke – veraltetes Image, fehlendes Datenbankpasswort, falsche EXTERNAL_URL – lassen sich mit dieser Anleitung sicher umschiffen. Da AFFiNE noch pre-1.0 ist, solltest du Updates im Staging testen, bevor du sie im Team ausrollst, und eine lückenlose Backup-Routine etablieren.

Weiterführende Anleitungen und Quellen

  1. Docker und Docker Compose auf Linux installieren – Grundlage für alle Docker-Deployments
  2. Traefik als Docker-Reverse-Proxy mit automatischem HTTPS – SSL für AFFiNE und andere Dienste
  3. MySQL & PostgreSQL Backup automatisieren mit cron – Backup-Automation für den AFFiNE-PostgreSQL-Stack
  4. Docker Compose absichern: Secrets, Healthchecks, Non-Root – Härtung für den Produktivbetrieb

Quellen: Alle technischen Details dieser Anleitung basieren auf der offiziellen AFFiNE-Dokumentation unter docs.affine.pro sowie dem GitHub-Repository toeverything/AFFiNE. Bei Widersprüchen zwischen Community-Anleitungen und offizieller Dokumentation gilt stets Letztere.