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

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

Twenty CRM ist eine Open-Source-Alternative zu Salesforce für KMU. Die Anleitung zeigt die Installation per Docker Compose mit PostgreSQL, Redis und Worker, getestet mit Twenty v2.45.0, samt HTTPS, Backup und Updates.

Geprüft am 04.10.2026 · für Twenty 2.45.0

Mit KI erstellt – redaktionelle Prüfung ausstehend

WerbelinksMit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Mehr dazu

Aufgeräumter Büroschreibtisch mit Telefon, leeren Karteikarten in einem Holzkasten und Ledernotizbuch

Bei Salesforce, HubSpot und ähnlichen Diensten liegen die Kundendaten in fremden Rechenzentren und die Kosten steigen mit jedem Nutzer. Twenty CRM ist eine Open-Source-Alternative mit Kontakt- und Deal-Verwaltung, anpassbaren Objekten, Workflows und GraphQL-API. Technisch setzt Twenty auf NestJS, React, PostgreSQL und Redis, das GitHub-Repository hat rund 57.000 Sterne. Diese Anleitung zeigt die Installation von Twenty CRM v2.45.0 per Docker Compose auf einem Linux-Host, mit Prüfung nach jedem Schritt und Backup.

Voraussetzungen

  1. Docker Engine (Version >= 20.10) und Docker Compose Plugin v2, Installation siehe Docker und Docker Compose auf Linux installieren (Ubuntu/Debian).
  2. Linux-Host, VM oder NAS mit Docker
  3. Mindestens 2 GB RAM laut Herstellerdoku, empfohlen 4 GB: Im Test belegten Server und Worker nach dem Start zusammen rund 2 GB, die Datenbank weitere 50 bis 80 MB. x86_64 oder ARM64, im Test mit 2 CPU-Kernen
  4. Mindestens 10 GB freier Festplattenspeicher (Images, Datenbankdaten, Uploads)
  5. openssl zur Schlüsselerzeugung
  6. Internetzugang für den Image-Pull
  7. Optional: eigene Domain mit DNS-Eintrag und Reverse Proxy (nginx, Traefik oder Caddy) für HTTPS. Ohne HTTPS sind Browser-APIs wie die Clipboard-API nicht nutzbar

Schritt 1: Projektordner anlegen

Legen Sie einen eigenen Ordner für den Stack an. Unter /opt brauchen Sie sudo und übergeben den Ordner danach Ihrem Benutzer:

sudo mkdir -p /opt/twenty-crm
sudo chown "$USER": /opt/twenty-crm
cd /opt/twenty-crm

Ohne Root-Zugriff nutzen Sie ~/twenty-crm. Verwenden Sie den gewählten Pfad durchgehend.

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 wichtigste Wert des Stacks. Er verschlüsselt die Secrets in der Datenbank: OAuth-Tokens, TOTP-Secrets und App-Variablen. Geht er verloren, sind diese Daten laut Herstellerdoku nicht mehr lesbar. Bewahren Sie ihn zusätzlich im Passwortmanager auf.

Erzeugen Sie zwei Zufallswerte:

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

# Datenbankpasswort (nur Buchstaben und Ziffern, keine Sonderzeichen)
openssl rand -hex 16

Legen Sie anschließend die .env an:

# Twenty CRM, Umgebungsvariablen
# ACHTUNG: ENCRYPTION_KEY nicht verlieren, Wechsel nur per Schlüsselrotation

TAG=v2.45.0

ENCRYPTION_KEY=IHR_BASE64_SCHLUESSEL

PG_DATABASE_USER=twenty
PG_DATABASE_PASSWORD=IHR_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

Ersetzen Sie IHR_BASE64_SCHLUESSEL und IHR_HEX_PASSWORT durch die erzeugten Werte und beschränken Sie die Rechte:

chmod 600 /opt/twenty-crm/.env

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

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

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

Schritt 3: compose.yaml erstellen

Der Stack besteht aus vier Services. Das offizielle Image twentycrm/twenty enthält Server und Frontend. Das ältere twentycrm/twenty-server ist auf Docker Hub archiviert (letztes Update März 2024).

Der Worker nutzt dasselbe Image mit yarn worker:prod und deaktiviert Migrationen und Cron-Registrierung, damit nichts doppelt läuft. Er bindet wie im offiziellen Compose-Beispiel dasselbe Volume server-local-data ein, damit Hintergrundjobs auf hochgeladene Dateien zugreifen können. Redis startet mit --maxmemory-policy noeviction, 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}
    volumes:
      - server-local-data:/app/packages/twenty-server/.local-storage
    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:

PostgreSQL und Redis haben bewusst kein Port-Mapping, nur Port 3000 ist 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 (Twenty v2.45.0 komprimiert rund 260 MB, dazu PostgreSQL und Redis). Danach führt der Server die Datenbankmigrationen aus. Im Test war der Server rund 100 Sekunden nach dem Start healthy, danach startete der Worker.

docker compose up -d

Startvorgang in den Logs verfolgen:

docker compose logs -f server

Steht dort Nest application successfully started, ist der Server bereit. Beenden Sie die Log-Ansicht mit Strg+C.

Verifizieren: Alle vier Services laufen, Server, Datenbank und Redis mit healthy:

docker compose ps
# Erwartete Ausgabe (gekürzt):
# NAME               IMAGE                    STATUS
# twenty-crm-server  twentycrm/twenty:v2.45.0 Up (healthy)
# twenty-crm-worker  twentycrm/twenty:v2.45.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

Öffnen Sie http://localhost:3000 (bzw. IP oder Domain Ihres Servers) im Browser. Beim ersten Aufruf legen Sie über den Einrichtungsassistenten das erste Konto an:

  1. Konto mit E-Mail-Adresse und starkem Passwort anlegen.
  2. Workspace einrichten (Name des Unternehmens oder Teams).
  3. Twenty öffnet danach die CRM-Oberfläche, Kontakte, Deals und anpassbare Objekte sind sofort nutzbar.
  4. Weitere Teammitglieder laden Sie über Settings → Members ein. SERVER_URL muss dafür auf die erreichbare Adresse zeigen, weil Einladungs-E-Mails diese URL verwenden.

Verifizieren: Die Anmeldung funktioniert und die Oberfläche zeigt den leeren Workspace. Auf der Kommandozeile liefert der folgende Aufruf den Seitentitel. Ohne den Accept-Header antwortete v2.43.0 im Test mit 404 Cannot GET /, obwohl der Browser die Seite anzeigte.

curl -s -H 'Accept: text/html' http://localhost:3000 | grep -o "<title>.*</title>"
# Erwartete Ausgabe: <title>Twenty</title>

Schritt 6: Reverse Proxy und HTTPS einrichten (Produktionsbetrieb)

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.

Beschränken Sie zuerst in der compose.yaml den Port auf localhost:

ports:
  - "127.0.0.1:3000:3000"

Setzen Sie SERVER_URL in der .env auf Ihre HTTPS-Domain:

SERVER_URL=https://crm.meinedomain.de

Schalten Sie dann einen Reverse Proxy davor, siehe Caddy als Reverse Proxy einrichten: Anfänger-Anleitung mit automatischem HTTPS. Ohne Portfreigabe eignet sich auch ein Cloudflare Tunnel.

Stack mit der geänderten Konfiguration neu erstellen:

docker compose up -d

Verifizieren: Die HTTPS-Adresse antwortet mit gültigem 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, Uploads im Volume server-local-data. Für die Datenbank genügt in den meisten KMU-Umgebungen ein täglicher pg_dump:

# 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
# Im Test: rund 8.000 Zeilen schon bei leerer Instanz

Ein 3-2-1-Konzept mit Rotation beschreibt MySQL & PostgreSQL Backup automatisieren mit cron: mysqldump, pg_dump, Rotation und rclone-Cloud-Sync.

Version-Update

Legen Sie vor jedem Update ein Backup an, tragen Sie den neuen Tag in die .env ein und aktualisieren Sie den Stack:

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

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

Der Server führt die Migrationen beim Start aus. Laut Upgrade-Guide sind ab v1.23 Sprünge über mehrere Versionen möglich, ab v2.34 ist PostgreSQL 15 Mindestvoraussetzung. Den Stand prüft docker compose exec server yarn command:prod upgrade:status, im Test mit v2.43.0 meldete er Instance: Up to date. Den Sprung von v2.43.0 auf v2.45.0 haben wir mit dem Stack dieser Anleitung getestet: Der Server registrierte die Upgrade-Befehle für 2.44 und 2.45 (cross-upgrade), meldete Nest application successfully started und war nach gut einer Minute wieder healthy. Ein zusätzlicher Aufruf von upgrade:status lief auf dem Testhost mit 3 GB RAM neben Server und Worker nicht durch; planen Sie dafür Speicherreserve ein. Die als Breaking Change markierte Änderung in v2.44 betrifft nur selbst entwickelte Twenty-Apps (Registrierungsvariablen nur noch über das Manifest), nicht den Betrieb per Compose.

Verifizieren: Der neue Tag ist aktiv, alle Services laufen wieder.

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.45.0 (Docker Hub, ca. 260 MB komprimiert, Stand Oktober 2026)
Veraltetes Imagetwentycrm/twenty-server, archiviert, 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, openssl rand -base64 32, nur per Rotation wechseln
Pflicht-Env 2PG_DATABASE_PASSWORD, ohne Sonderzeichen
Pflicht-Env 3SERVER_URL, vollständige URL mit Schema und ggf. Port
Mindest-RAM2 GB (empfohlen: 4 GB)
GitHub-Starsrund 57.000 (Stand Oktober 2026)

Troubleshooting / Typische Fehler

  1. Container stürzt beim Start ab (OOMKilled): Zu wenig RAM. Prüfen Sie mit docker stats oder docker compose logs server. Lösung: mindestens 2 GB, besser 4 GB RAM, notfalls eine Swap-Datei.
  2. Datenbankverbindung schlägt fehl: Meist enthalten PG_DATABASE_PASSWORD Sonderzeichen wie @, # oder /, die in der Verbindungs-URL falsch ankommen. Verwenden Sie nur Buchstaben und Ziffern, etwa per openssl rand -hex 16.
  3. Worker startet nicht oder hängt in depends_on: Er wartet auf den Server-Healthcheck, beim ersten Start laufen noch Migrationen. Warten Sie 2 bis 3 Minuten oder erhöhen Sie start_period im Server-Healthcheck auf 120s.
  4. OAuth-Callbacks schlagen fehl oder Einladungslinks sind falsch: SERVER_URL passt nicht zur erreichbaren Adresse. Korrigieren (inklusive https://) und Stack neu starten.
  5. Browserfunktionen wie Zwischenablage fehlen: Die Seite läuft ohne HTTPS. Reverse Proxy mit TLS vorschalten (Schritt 6).
  6. Altes Image twentycrm/twenty-server verwendet: Frontend fehlt oder ist veraltet. Wechseln Sie auf twentycrm/twenty.
  7. Daten nach docker compose down verloren: Vermutlich lief docker compose down -v. -v löscht die benannten Volumes, ohne -v bleiben db-data und server-local-data erhalten.
  8. ENCRYPTION_KEY verloren: OAuth-Tokens, TOTP-Secrets und App-Variablen sind nicht mehr lesbar, eine Wiederherstellung gibt es nicht. Einen Schlüsselwechsel führen Sie nur über die Schlüsselrotation laut Herstellerdoku durch (alter Schlüssel in FALLBACK_ENCRYPTION_KEY).

Häufige Fragen

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

Ja. Laut Docker Hub gibt es twentycrm/twenty:v2.45.0 für amd64 und arm64. Getestet wurde nur amd64.

Wie update ich Twenty auf eine neue Version?

Siehe Schritt 7: Backup, neuen Tag in der .env, dann docker compose pull && docker compose up -d.

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

docker compose down ohne Optionen entfernt nur die Container, die Volumes db-data und server-local-data bleiben. Erst docker compose down -v löscht sie mit allen Daten.

Wie richte ich S3-Speicher statt lokalem Storage ein?

Setzen Sie in der .env STORAGE_TYPE=s3 sowie STORAGE_S3_REGION, STORAGE_S3_NAME, STORAGE_S3_ENDPOINT und die Zugangsdaten. S3-kompatible Speicher wie Hetzner Object Storage oder Garage funktionieren ebenfalls, siehe S3-Speicher selbst hosten nach dem MinIO-Aus: Garage und RustFS im Vergleich.

Wie setze ich das Admin-Passwort zurück?

Die Login-Seite bietet „Passwort vergessen“ per E-Mail, das setzt einen konfigurierten Mailversand voraus. Ohne Mailserver bleibt der Weg über die Datenbank: docker compose exec db psql -U twenty -d default, die Konten liegen in der Tabelle core."user" (Spalte "passwordHash", bcrypt). Legen Sie vor einem Eingriff ein Backup an.

Wie integriere ich Google-Kalender und Gmail?

Setzen Sie MESSAGING_PROVIDER_GMAIL_ENABLED=true und CALENDAR_PROVIDER_GOOGLE_ENABLED=true und hinterlegen Sie Client-ID und Client-Secret einer Google-OAuth-App aus der Google Cloud Console. Im offiziellen Compose-Beispiel sind diese Variablen auskommentiert, Sie müssen sie in der compose.yaml bei Server und Worker aktivieren. SERVER_URL muss auf eine öffentlich erreichbare HTTPS-Adresse zeigen.

Fazit

Twenty CRM ist eine Alternative zu Salesforce und HubSpot, bei der die Daten auf dem eigenen Server bleiben. Die Installation per Compose ist schnell erledigt, wenn ENCRYPTION_KEY, PG_DATABASE_PASSWORD und SERVER_URL stimmen. Sichern Sie den Encryption Key extern, schalten Sie für den Produktivbetrieb einen Reverse Proxy mit HTTPS davor und automatisieren Sie das Datenbank-Backup.

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: Docker-Compose-Dokumentation · Upgrade-Guide · GitHub-Repository twentyhq/twenty · Docker Hub: twentycrm/twenty