Customermates auf eigener Infra betreiben. Wann Self-Host sinnvoll ist, wie du es mit Docker Compose in unter 15 Minuten installierst, und wie du es betreibst.
Self-Hosting sind zwei Dateien (docker-compose.yml und .env) plus docker compose up -d. Beide Dateien liegen im Customermates-Repo und du holst sie mit curl. Kein git clone, kein Build-Schritt. Das veröffentlichte Image unter ghcr.io/customermates/customermates:latest führt Migrationen beim ersten Boot aus und ist sofort einsatzbereit.
Beide haben denselben Funktionsumfang.
| Wenn du… | Nimm |
|---|---|
| Heute noch mit dem CRM arbeiten willst | Cloud |
| Ein Team von < 20 ohne harte Data-Residency-Anforderung | Cloud |
| Das CRM auf Private-Infra für Compliance brauchst | Self-Host |
| Long-term weg von Pro-Seat-Pricing willst | Self-Host |
| Agentur mit mehreren Mandanten bist | Self-Host, eine Instanz pro Mandant |
| Zum Projekt beitragen willst | Self-Host lokal, Cloud für echte Arbeit |
| Cloud | Self-Host | |
|---|---|---|
| Setup-Zeit | 2 Minuten | ~15 Minuten |
| Infra, die du managst | keine | Docker, Postgres, Proxy, TLS, Backups |
| Updates | automatisch | docker compose pull && docker compose up -d |
| EU-gehostet | ✓ | wo du willst |
| Backups | automatisch täglich | du konfigurierst |
| Support-SLA | inkl. in Paid-Plans | Community |
| Enterprise-Features (SSO, Audit-Log) | Paid-Plan | Paid-License-Key |
| Pricing | pro Sitz | kostenlos, nur Enterprise-Lizenz zahlen |
Datenhoheit: Die Cloud speichert Daten in unserer EU-Region, DSGVO-konform, mit üblicher Verschlüsselung at-rest und in-transit. Wenn deine Compliance verlangt, dass Daten auf deiner Infra liegen: Self-Host.
Kostenform: Cloud ist vorhersagbar pro Sitz und linear skalierend. Self-Host ist kostenlos für den Core plus was du für Infra zahlst (ein kleiner VPS trägt Hunderte User). Für ein 5er-Team ist Cloud meist günstiger, wenn du die Zeit fürs eigene Postgres-Backup einrechnest. Für ein 50er-Team lohnt sich Self-Host schnell.
Du kannst zwischen beiden wechseln. Export von einem, Import ins andere. Identisches Datenmodell. Kein Lock-in in beide Richtungen.
mkdir customermates && cd customermates
curl -fsSL https://raw.githubusercontent.com/customermates/customermates/main/docker-compose.yml -o docker-compose.yml
curl -fsSL https://raw.githubusercontent.com/customermates/customermates/main/.env.selfhost.template -o .envAnschließend .env mit echten Werten editieren:
BETTER_AUTH_SECRET: langer Zufallsstring (openssl rand -hex 32).CRON_SECRET: weiterer Zufallsstring. Der webhook-worker-Sidecar nutzt ihn, um sich gegen /api/cron/webhook-deliveries zu authentifizieren. Mismatch heißt: Webhooks bleiben für immer in pending hängen.POSTGRES_PASSWORD: default ändern.BASE_URL: deine Public-URL (z.B. https://crm.example.com). Lokal default http://localhost:4000.RESEND_API_KEY und RESEND_FROM_EMAIL: nötig für Signup-Verifizierung, Passwort-Reset und Einladungs-Mails.docker compose up -dFirst-Boot dauert eine Minute, während Prisma-Migrationen laufen. Logs ansehen:
docker compose logs -f appWenn die App ready ist, http://localhost:4000 öffnen (oder dein eigenes APP_PORT).
URL öffnen. Mit E-Mail registrieren, Verifizierungs-Link aus dem Postfach klicken, dann Workspace-Namen wählen. Weitere User und Rollen verwalten über Unternehmen → Benutzer und Unternehmen → Rollen.
Reverse-Proxy (Caddy, nginx, Traefik) auf den App-Port zeigen (4000 by default, oder dein eigenes APP_PORT). Caddy:
crm.example.com {
reverse_proxy localhost:4000
}Customermates setzt Secure-Cookies, wenn BASE_URL mit https:// startet. Der Proxy muss X-Forwarded-Proto korrekt weiterreichen.
Profil → API Keys → Neuer Key. Genau wie in der Cloud. Siehe API-Keys.
docker compose pull
docker compose up -dPullt das neueste App-Image und startet die betroffenen Services neu. Migrationen laufen beim Container-Boot automatisch. Status mit docker compose ps prüfen.
docker compose restartStartet den Stack ohne neues Image-Pull neu. Nach .env-Änderungen.
docker compose logs -f app
docker compose logs -f postgres
docker compose logs -f webhook-worker
docker compose ps
docker compose exec app shDer webhook-worker-Container bleibt bei Erfolg still und loggt jede Nicht-2xx-Antwort ([webhook-worker] HH:MM:SSZ cron returned 401). Wenn solche Zeilen auftauchen, passt das CRON_SECRET des Workers nicht zum App-Container, oder die Cron-Route ist sonstwie nicht erreichbar. Hängen Deliveries in pending und die Worker-Logs sind leer, ist der Worker gesund.
docker compose down -v
docker compose up -d-v löscht das Postgres-Volume. IRREVERSIBLE. Vorher Backup ziehen, falls die Daten benötigt werden.
Postgres per pg_dump auf Zeitplan. Der App-Container ist stateless.
docker compose exec -T postgres pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB" \
| gzip > /var/backups/customermates-$(date +%Y%m%d).sql.gzFür Produktion:
.env und Secrets nicht ins Source-Repo.Audit-Logging, SSO und White-Labeling brauchen eine Enterprise-Lizenz. Alles andere ist kostenlos. Lizenz gemäß der Anleitung konfigurieren, die mit deinem Lizenzschlüssel geliefert wird.