Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung IT-Branche 20.08.2026 · 11 min Lesezeit

Twenty CRM mit Docker installieren: Modernes Open-Source-CRM als Salesforce-Alternative

Twenty CRM ist mit knapp 50.000 GitHub-Stars das am schnellsten wachsende Open-Source-CRM – eine datenschutzkonforme Salesforce-Alternative für KMU, die du per Docker Compose in 20 Minuten auf deinem eigenen Server betreibst und dabei vollständige Datenkontrolle behältst.

Twenty CRM mit Docker installieren: Modernes Open-Source-CRM als Salesforce-Alternative mit CRM-Dashboard, Kundenverwaltung, Deals, Pipelines und Docker-Container. KI-generiert

Salesforce, HubSpot und Co. kosten schnell hunderte Euro pro Monat – und die Kundendaten liegen dabei in fremden Rechenzentren. Twenty CRM ist die moderne Open-Source-Antwort darauf: ein vollwertiges CRM mit Kontakt- und Deal-Management, anpassbaren Objekten, Workflows, KI-Agenten-Chat und einer GraphQL-API. Technologisch setzt Twenty auf NestJS, React, PostgreSQL und Redis – und mit knapp 50.000 GitHub-Stars ist es das am schnellsten wachsende Open-Source-CRM überhaupt. Das macht es zu einer ernstzunehmenden Alternative für KMU, Entwicklungsteams und alle, die ihre CRM-Daten lieber selbst kontrollieren wollen. Diese Anleitung zeigt dir, wie du Twenty CRM v2.11.0 per Docker Compose auf einem beliebigen Linux-Host installierst – mit korrekter Konfiguration, Healthchecks nach jedem Schritt und einem soliden Backup-Konzept.

Voraussetzungen

  1. Docker Engine (Version >= 20.10) und Docker Compose Plugin v2 auf dem Host installiert – eine vollständige Installationsanleitung findest du unter Docker und Docker Compose auf Linux installieren (Ubuntu/Debian).
  2. Linux-Host, VM oder NAS mit Docker-Unterstützung (Ubuntu, Debian oder kompatibel)
  3. Mindestens 2 GB RAM – mit weniger kommt es zu Prozessabbrüchen (OOMKilled); empfohlen sind 4 GB für ruhigen Betrieb
  4. Mindestens 10 GB freier Festplattenspeicher (Images, Datenbankdaten, Uploads)
  5. openssl zur Schlüsselgenerierung (auf jedem Linux-System vorinstalliert)
  6. Internetverbindung für den Image-Pull von Docker Hub
  7. Optional: eigene Domain mit DNS-Eintrag und ein Reverse Proxy (nginx, Traefik oder Caddy) für HTTPS – ohne HTTPS sind Browser-APIs wie die Clipboard-API im Produktionsbetrieb nicht nutzbar

Schritt 1: Projektordner anlegen

Erstelle einen dedizierten Ordner für den Twenty-CRM-Stack. Alle Konfigurationsdateien leben hier – damit bleibt alles übersichtlich und lässt sich leicht sichern.

mkdir -p /opt/twenty-crm
cd /opt/twenty-crm

Du kannst auch ~/twenty-crm verwenden, wenn du keinen Root-Zugriff hast. Wichtig ist nur, dass du denselben Pfad konsistent weiterverwendest.

Verifizieren: Der Ordner existiert und ist das aktuelle Arbeitsverzeichnis.

pwd
# Erwartete Ausgabe: /opt/twenty-crm

Schritt 2: .env-Datei mit Secrets anlegen

Der ENCRYPTION_KEY ist der kritischste Konfigurationswert des gesamten Stacks. Er verschlüsselt alle Secrets in der Datenbank – OAuth-Tokens, TOTP-Secrets und App-Variablen. Verlierst du diesen Schlüssel oder änderst ihn nachträglich, sind diese Daten dauerhaft unlesbar. Bewahre ihn deshalb zusätzlich in einem Passwort-Manager auf.

Erzeuge zunächst zwei starke Zufallsschlüssel:

# Verschlüsselungsschlüssel (Pflicht – sicher aufbewahren!)
openssl rand -base64 32

# Datenbankpasswort (nur alphanumerische Zeichen + Bindestriche – keine Sonderzeichen!)
openssl rand -hex 16

Erstelle anschließend die .env-Datei mit den erzeugten Werten:

# Twenty CRM – Umgebungsvariablen
# ACHTUNG: ENCRYPTION_KEY niemals ändern oder verlieren!

TAG=v2.11.0

ENCRYPTION_KEY=DEIN_ERZEUGTER_BASE64_SCHLUESSEL

PG_DATABASE_USER=twenty
PG_DATABASE_PASSWORD=DEIN_ERZEUGTES_HEX_PASSWORT

# Für lokalen Betrieb:
SERVER_URL=http://localhost:3000

# Für externen Betrieb (Domain anpassen):
# SERVER_URL=https://crm.meinedomain.de

STORAGE_TYPE=local

Tausche DEIN_ERZEUGTER_BASE64_SCHLUESSEL und DEIN_ERZEUGTES_HEX_PASSWORT durch die tatsächlich generierten Werte aus. Setze außerdem die Dateiberechtigung restriktiv:

chmod 600 /opt/twenty-crm/.env

Verifizieren: Die Datei existiert, enthält keine Platzhalter mehr und ist nur für den Eigentümer lesbar.

ls -la .env
# Erwartete Ausgabe: -rw------- 1 root root ... .env

grep -c "DEIN_" .env
# Erwartete Ausgabe: 0 (keine Platzhalter mehr)

Schritt 3: compose.yaml erstellen

Der Stack besteht aus genau vier Services. Das twentycrm/twenty-Image (das einzige offizielle, aktiv gepflegte Image) enthält Server und Frontend in einem Container – ältere Anleitungen, die twentycrm/twenty-server referenzieren, beziehen sich auf ein archiviertes, nicht mehr gepflegtes Image.

Der Worker-Service nutzt dasselbe Image, startet aber mit yarn worker:prod und deaktiviert DB-Migrationen sowie Cron-Job-Registrierung, um Doppelausführungen zu vermeiden. Redis wird mit --maxmemory-policy noeviction gestartet, damit die Job-Queue bei Speicherdruck keine Keys verliert.

services:
  server:
    image: twentycrm/twenty:${TAG:-latest}
    ports:
      - "3000:3000"
    volumes:
      - server-local-data:/app/packages/twenty-server/.local-storage
    environment:
      NODE_PORT: 3000
      SERVER_URL: ${SERVER_URL:-http://localhost:3000}
      REDIS_URL: redis://redis:6379
      PG_DATABASE_URL: postgres://${PG_DATABASE_USER:-twenty}:${PG_DATABASE_PASSWORD}@db:5432/default
      STORAGE_TYPE: ${STORAGE_TYPE:-local}
      ENCRYPTION_KEY: ${ENCRYPTION_KEY}
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/healthz"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 60s
    restart: always

  worker:
    image: twentycrm/twenty:${TAG:-latest}
    command: ["yarn", "worker:prod"]
    environment:
      SERVER_URL: ${SERVER_URL:-http://localhost:3000}
      REDIS_URL: redis://redis:6379
      PG_DATABASE_URL: postgres://${PG_DATABASE_USER:-twenty}:${PG_DATABASE_PASSWORD}@db:5432/default
      STORAGE_TYPE: ${STORAGE_TYPE:-local}
      ENCRYPTION_KEY: ${ENCRYPTION_KEY}
      DISABLE_DB_MIGRATIONS: "true"
      DISABLE_CRON_JOBS_REGISTRATION: "true"
    depends_on:
      db:
        condition: service_healthy
      server:
        condition: service_healthy
    restart: always

  db:
    image: postgres:16
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: default
      POSTGRES_USER: ${PG_DATABASE_USER:-twenty}
      POSTGRES_PASSWORD: ${PG_DATABASE_PASSWORD}
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${PG_DATABASE_USER:-twenty} -d default"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: always

  redis:
    image: redis
    command: ["--maxmemory-policy", "noeviction"]
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: always

volumes:
  db-data:
  server-local-data:

Beachte: PostgreSQL (Port 5432) und Redis (Port 6379) haben absichtlich kein Port-Mapping nach außen – das ist eine bewusste Sicherheitsentscheidung. Nur Port 3000 ist von außen erreichbar.

Verifizieren: Die YAML-Syntax ist korrekt und docker compose erkennt die Datei.

docker compose config --quiet
# Keine Ausgabe = kein Syntaxfehler

Schritt 4: Stack starten

Beim ersten Start lädt Docker die Images herunter (ca. 235 MB für das Twenty-Image plus PostgreSQL und Redis). Danach führt der Server automatisch alle Datenbankmigrationen durch – das dauert beim allerersten Start typischerweise 1–2 Minuten länger als bei späteren Starts.

docker compose up -d

Verfolge den Startvorgang live in den Logs:

docker compose logs -f server

Wenn du eine Zeile wie Server is running on port 3000 oder einen HTTP-200-Response auf /healthz siehst, ist der Server bereit. Beende die Log-Ansicht mit Strg+C.

Verifizieren: Alle vier Services laufen mit Status Up bzw. healthy:

docker compose ps
# Erwartete Ausgabe (gekürzt):
# NAME               IMAGE                    STATUS
# twenty-crm-server  twentycrm/twenty:v2.11.0 Up (healthy)
# twenty-crm-worker  twentycrm/twenty:v2.11.0 Up
# twenty-crm-db      postgres:16              Up (healthy)
# twenty-crm-redis   redis                    Up (healthy)

curl -I http://localhost:3000/healthz
# Erwartete Ausgabe: HTTP/1.1 200 OK

Schritt 5: Erst-Einrichtung im Browser

Öffne http://localhost:3000 (bzw. die IP/Domain deines Servers) im Browser. Beim ersten Aufruf zeigt Twenty einen Setup-Assistenten, über den du das Admin-Konto anlegst.

  1. Klicke auf „Create your account" und gib E-Mail-Adresse sowie ein starkes Passwort ein.
  2. Im nächsten Schritt richtest du deinen Workspace ein (Name deines Unternehmens oder Teams).
  3. Twenty leitet dich direkt in die CRM-Oberfläche weiter – Kontakte, Deals und die anpassbaren Objekte sind sofort nutzbar.
  4. Weitere Teammitglieder lädst du über Settings → Members ein. Dafür muss SERVER_URL korrekt auf deine erreichbare Adresse gesetzt sein, da die Einladungs-E-Mails diese URL verwenden.

Verifizieren: Die Anmeldung funktioniert, die CRM-Oberfläche lädt vollständig und zeigt den leeren Workspace.

curl -s http://localhost:3000 | grep -o "<title>.*</title>"
# Erwartete Ausgabe enthält: Twenty

Schritt 6: Reverse Proxy und HTTPS einrichten (Produktionsbetrieb)

Für den Produktionsbetrieb – also sobald Twenty über eine externe Domain erreichbar sein soll – ist HTTPS Pflicht. Ohne TLS funktionieren Browser-APIs wie die Clipboard-API nicht, und OAuth-Integrationen schlagen fehl.

Schränke den Port zunächst auf localhost ein, damit Twenty nicht ungesichert im Internet erreichbar ist. Ändere in der compose.yaml das Port-Mapping:

ports:
  - "127.0.0.1:3000:3000"

Passe anschließend die SERVER_URL in der .env auf deine HTTPS-Domain an:

SERVER_URL=https://crm.meinedomain.de

Schalte dann einen Reverse Proxy davor. Wie du Caddy, nginx oder Traefik als Reverse Proxy mit automatischem HTTPS konfigurierst, erklärt die Anleitung Caddy als Reverse Proxy einrichten: Anfänger-Anleitung mit automatischem HTTPS. Für selbstgehostete Setups ohne Portfreigabe eignet sich außerdem ein Cloudflare Tunnel als Zero-Exposure-Alternative.

Starte den Stack nach der Änderung neu:

docker compose up -d

Verifizieren: Die HTTPS-URL ist erreichbar und liefert ein gültiges Zertifikat.

curl -I https://crm.meinedomain.de/healthz
# Erwartete Ausgabe: HTTP/2 200

Schritt 7: Backup und Updates

Datenbank-Backup

Twenty speichert alle CRM-Daten in PostgreSQL. Ein täglicher pg_dump reicht für die meisten KMU-Setups aus:

# Einmaliges Backup
docker exec $(docker compose ps -q db) \
  pg_dump -U twenty default > backup_$(date +%Y%m%d).sql

# Backup prüfen
wc -l backup_$(date +%Y%m%d).sql
# Mindestens einige tausend Zeilen bei laufendem System

Für ein vollständiges 3-2-1-Backup-Konzept mit automatischer Rotation empfiehlt sich die Anleitung MySQL & PostgreSQL Backup automatisieren mit cron: mysqldump, pg_dump, Rotation und rclone-Cloud-Sync.

Version-Update

Lege vor jedem Update ein Backup an. Setze dann in der .env den neuen Tag und aktualisiere den Stack:

# 1. Backup anlegen (wie oben)
# 2. Neue Version in .env eintragen:
#    TAG=v2.12.0  (Beispiel)

# 3. Images pullen und Stack neu starten
docker compose pull && docker compose up -d

Der Server führt Datenbankmigrationen beim Start automatisch durch.

Verifizieren: Der neue Image-Tag ist aktiv und alle Services laufen wieder healthy.

docker compose ps
docker inspect $(docker compose ps -q server) | grep -i "image"
# Zeigt den neuen Image-Tag

Eckdaten auf einen Blick

EigenschaftWert
Aktuelles Imagetwentycrm/twenty:v2.11.0 (Docker Hub, ~235 MB)
Veraltetes Imagetwentycrm/twenty-server – archived, nicht verwenden
Externer Port3000 (Web-UI + API)
Interne Ports5432 (PostgreSQL), 6379 (Redis) – kein Mapping nach außen
Pflicht-Volume 1db-data → PostgreSQL-Datenbankdateien
Pflicht-Volume 2server-local-data → Uploads/Attachments
Pflicht-Env 1ENCRYPTION_KEY – base64, 32 Zeichen, nie ändern
Pflicht-Env 2PG_DATABASE_PASSWORD – nur alphanumerisch + Bindestriche
Pflicht-Env 3SERVER_URL – vollständige URL inkl. Schema und Port
Mindest-RAM2 GB (empfohlen: 4 GB)
GitHub-Starsknapp 50.000 (Stand Juni 2026)

Troubleshooting / Typische Fehler

  1. Container crasht beim Start (OOMKilled): Der Host hat zu wenig RAM. Prüfe mit docker stats oder docker compose logs server. Lösung: Mindestens 2 GB RAM sicherstellen; im Zweifelsfall eine Swap-Datei einrichten.
  2. Datenbankverbindung schlägt fehl: Häufige Ursache sind Sonderzeichen in PG_DATABASE_PASSWORD (z. B. @, #, /). Diese werden in der Connection-URL falsch kodiert. Nur alphanumerische Zeichen und Bindestriche verwenden – im Zweifelsfall ein neues Passwort per openssl rand -hex 16 erzeugen.
  3. Worker startet nicht / hängt in depends_on: Der Worker wartet auf den Server-Healthcheck (/healthz). Beim ersten Start dauert es länger, weil Datenbankmigrationen laufen. Einfach 2–3 Minuten abwarten; der Worker startet dann automatisch. Alternativ start_period im Server-Healthcheck auf 120s erhöhen.
  4. OAuth-Callbacks schlagen fehl / Einladungs-Links falsch: SERVER_URL stimmt nicht mit der tatsächlich erreichbaren Adresse überein. Korrekten Wert in der .env setzen (inkl. Schema: https://) und Stack neu starten.
  5. Browser-Features funktionieren nicht (Clipboard etc.): Die Seite wird ohne HTTPS ausgeliefert. Reverse Proxy mit TLS-Terminierung vorschalten (siehe Schritt 6).
  6. Altes Image twentycrm/twenty-server verwendet: Frontend-Seiten fehlen oder Funktionen sind veraltet. Auf twentycrm/twenty (All-in-One) wechseln.
  7. Daten nach docker compose down verloren: Du hast vermutlich docker compose down -v ausgeführt. Das -v-Flag löscht named Volumes. Ohne -v bleiben db-data und server-local-data immer erhalten.
  8. ENCRYPTION_KEY verloren oder geändert: Alle OAuth-Tokens, TOTP-Secrets und App-Variablen in der Datenbank sind dauerhaft unlesbar. Es gibt keine Wiederherstellung. Den Schlüssel extern (Passwort-Manager) sichern und niemals nachträglich ändern.

Häufige Fragen

Läuft Twenty CRM auf ARM64 (Apple Silicon, Raspberry Pi)?

Das Image twentycrm/twenty wird auf Docker Hub als Multi-Arch-Build veröffentlicht. AMD64 (x86_64) ist offiziell unterstützt. Für ARM64 lohnt ein Test-Pull (docker pull twentycrm/twenty) – wenn kein passendes Manifest vorhanden ist, hilft QEMU-Emulation oder ein Build aus dem Quellcode.

Wie update ich Twenty auf eine neue Version?

Leg zuerst ein Datenbank-Backup an (siehe Schritt 7). Dann in der .env den Tag auf die neue Version setzen (z. B. TAG=v2.12.0) und anschließend docker compose pull && docker compose up -d ausführen. Der Server führt alle erforderlichen Datenbankmigrationen beim Start automatisch durch.

Was passiert, wenn ich docker compose down ausführe?

Mit docker compose down ohne Flags werden nur die Container gestoppt und entfernt – die named Volumes db-data und server-local-data bleiben unangetastet. Erst docker compose down -v würde die Volumes und damit alle Daten unwiderruflich löschen. Diesen Befehl nur bei einem bewussten Komplett-Reset verwenden.

Wie richte ich S3-Speicher statt lokalem Storage ein?

In der .env STORAGE_TYPE=s3 setzen und zusätzlich STORAGE_S3_REGION, STORAGE_S3_NAME und STORAGE_S3_ENDPOINT sowie AWS-Credentials konfigurieren. S3-kompatibler Storage wie MinIO, Hetzner Object Storage oder Garage funktioniert ebenfalls. Eine Anleitung zum Selbsthosten von S3-kompatiblem Storage bietet S3-Speicher selbst hosten nach dem MinIO-Aus: Garage und RustFS im Vergleich.

Wie setze ich das Admin-Passwort zurück?

Twenty bietet auf der Login-Seite eine „Passwort vergessen"-Funktion per E-Mail. Ohne konfigurierten Mail-Server kann das Passwort direkt per SQL zurückgesetzt werden: docker exec -it <db-container> psql -U twenty -d default und dann den Passwort-Hash in der users-Tabelle ersetzen. Den Container-Namen findest du mit docker compose ps.

Wie integriere ich Google-Kalender und Gmail?

Google-Integrationen aktivierst du in der .env mit MESSAGING_PROVIDER_GMAIL_ENABLED=true und CALENDAR_PROVIDER_GOOGLE_ENABLED=true. Dafür benötigst du außerdem eine Google-OAuth-App (Google Cloud Console) und die zugehörigen Client-ID und Client-Secret als weitere Umgebungsvariablen. SERVER_URL muss auf eine öffentlich erreichbare HTTPS-URL zeigen, damit die OAuth-Callbacks funktionieren.

Fazit

Twenty CRM bietet KMU eine technisch ausgereifte, datenschutzkonforme Alternative zu Salesforce und HubSpot – ohne monatliche Lizenzkosten und ohne Abhängigkeit von externen Cloud-Diensten. Die Docker-Compose-Installation ist in 20 Minuten erledigt, wenn die drei Pflicht-Variablen (ENCRYPTION_KEY, PG_DATABASE_PASSWORD, SERVER_URL) korrekt gesetzt sind. Der wichtigste Hinweis gilt dem Encryption Key: Einmal gesetzt, niemals ändern und sicher extern aufbewahren. Mit v2.11.0 hat Twenty den Reifegrad erreicht, um als produktive CRM-Lösung für Teams mit vollständiger Datenkontrolle zu dienen. Wer den nächsten Schritt gehen will, sichert den Stack mit Reverse Proxy, HTTPS und automatisiertem Datenbank-Backup ab.

Weiterführende Anleitungen und Quellen

  1. Docker und Docker Compose auf Linux installieren (Ubuntu/Debian): die Self-Hosting-Grundlage
  2. Caddy als Reverse Proxy einrichten: Anfänger-Anleitung mit automatischem HTTPS
  3. MySQL & PostgreSQL Backup automatisieren mit cron: pg_dump, Rotation und rclone-Cloud-Sync
  4. Plane mit Docker installieren: Self-Hosted-Projektmanagement (Batch-Geschwister)
  5. S3-Speicher selbst hosten nach dem MinIO-Aus: Garage und RustFS im Vergleich

Offizielle Quellen: Twenty CRM – Offizielle Docker-Compose-Dokumentation · GitHub-Repository twentyhq/twenty · Docker Hub: twentycrm/twenty