Blog
Tutorials

Simmetric Chat mit Docker deployen: der komplette Leitfaden

Die vollständige Deployment-Landkarte für Simmetric Chat - ein Compose-Stack aus sechs Diensten, ein einzelner All-in-One-Container für Air-Gap-Hosts und der Ein-Klick-Weg über Coolify.

Simmetric Team7 Min. Lesezeit10/03/2026

Ein Produkt, drei Deployment-Formen

Simmetric Chat ist eine Multi-Service-Plattform - ein API-Server, ein Collector für die Dokumentenverarbeitung, ein einbettbarer Widget-Dienst, ein Frontend und eine PostgreSQL-Datenbank -, daher geht es beim Deployment darum, das richtige Packaging für Ihre Umgebung zu wählen, nicht Dienste manuell zu verdrahten. Es gibt drei Produktionswege, und alle Artefakte liegen im Verzeichnis docker/ des Repositories:

  • Multi-Container-Compose - sechs Dienste, jeder im eigenen Container mit Healthcheck. Das empfohlene Produktionslayout mit Isolation pro Dienst und unabhängigen Neustarts.
  • Einzelner All-in-One-Container - ein einziges Image, das alles ausführt: Postgres, Server, Collector und Widget, überwacht von supervisord. Gedacht für Air-Gap-Hosts und Demo-Maschinen, wo das Bewegen mehrerer Images durch den Perimeter der teure Teil ist.
  • Coolify - ein self-hosted PaaS-Weg mit TLS-Terminierung durch Traefik, bei dem die Plattform aus einer auf Coolify-Konventionen zugeschnittenen Compose-Datei für Sie geklont und gebaut wird.

Alle Images sind Multi-Stage-Builds auf Node-24-Basen, und jeder Dienst exponiert einen Healthcheck, von dem die Startreihenfolge des Stacks abhängt. Dieser Artikel geht jeden Weg durch, das Konfigurationsmodell und die Details, die in der Produktion beißen.

Der Compose-Stack aus sechs Diensten

Die Produktions-Compose-Datei definiert sechs aktive Dienste:

  • frontend - gebaut als React-19-App aus einem Node-Builder, dann von nginx serviert, das TLS mit beim Image-Build generierten selbstsignierten Zertifikaten terminiert, die SPA ausliefert und /api und /widget zu den Backend-Diensten proxyt.
  • server - die Express-5-API: Authentifizierung, RBAC, Chat-Orchestrierung und der ReAct-Agent. Mit diesem Dienst sprechen Ihre Nutzer.
  • collector - die Dokumentenpipeline: Parsing, Chunking und Embedding der Uploads. Gebaut auf einer Slim-Node-Basis statt Alpine, weil seine nativen Abhängigkeiten (LanceDB, die lokalen Embedding-Transformers, die Canvas-Bindings) glibc erfordern.
  • widget - der Dienst des einbettbaren Chat-Widgets für nach außen gerichtete Websites.
  • postgres - PostgreSQL 16 mit der pgvector-Erweiterung inklusive, damit der Standard-Vektorprovider keinen separaten Vektor-Datenbankdienst braucht.
  • redis - der optionale Skalierungs-Cache, aktiviert mit Append-only-Persistenz und LRU-Eviction. Jeder Redis-Konsument degradiert in den Speicher, daher kann dieser Dienst dabei sein oder fehlen, ohne Probleme zu machen.

Zwei Dienste sind in der Standarddatei bewusst deaktiviert statt abwesend: ollama und qdrant. Ihre Blöcke kommen auskommentiert, und sie zu aktivieren heißt, eine Zeile plus eine Variable zu entkommentieren - OLLAMA_BASE_URL=http://ollama:11434 für den Host der lokalen Modelle, oder VECTOR_DB_PROVIDER=qdrant, wenn Sie einen entfernten Vektorspeicher statt des dateibasierten LanceDB-Standards wollen. Die auskommentierten Standards halten eine minimale Installation wirklich minimal.

Die Startreihenfolge wird von den Healthchecks bestimmt: wget-basierte Sonden treffen GET /api/health auf Server und Collector, /health auf dem Widget, pg_isready auf Postgres und redis-cli ping auf Redis, und das Frontend wartet, bis der Server sich als healthy meldet, bevor es startet.

Den Stack hochzufahren ist ein einziger Befehl:

bash
docker compose -f docker/docker-compose.yml up --build -d

Der einzelne All-in-One-Container

Der zweite Weg komprimiert die gesamte Plattform in ein einziges Image. Ein einzelnes Dockerfile kompiliert PostgreSQL 16 mit der beim Image-Build aus dem Quellcode gebauten pgvector-Erweiterung, und supervisord führt im Container Datenbank, Server, Collector und Widget gemeinsam aus. Die Ports 3000, 3210 und 3211 sind exponiert, und der Healthcheck fragt /api/health des Servers auf localhost ab.

bash
docker build -f docker/Dockerfile -t simmetric-chat-all-in-one .
docker run -d -p 3000:3000 -p 3210:3210 -p 3211:3211 simmetric-chat-all-in-one

Das All-in-One-Image ist die Air-Gap-Geschichte: ein einziges Artefakt, das den Perimeter überqueren muss, kein Registry-Pull zum Installationszeitpunkt, und alles - von der Datenbank bis zur Chat-UI - startet mit einem einzigen docker run. Die pgvector-Quellen werden während des Builds offline kompiliert und installiert, genau damit das resultierende Image zur Installationszeit keine Netzabhängigkeiten hat. Seine Umgebungsstandards sind bewusst Gerüst-Platzhalter - der Betreiber überschreibt die echten Geheimnisse per .env oder -e, und das Image gibt nie vor, out of the box produktionssicher zu sein.

Eine Grenze lohnt sich zu kennen: Das Enterprise-Plugin wird nie in ein Image eingebacken. Das ist Absicht - es hält die AGPL-Images der Community frei von proprietärem Code und erlaubt Air-Gap-Kunden, das Plugin als separates, prüfbares Tarball zu erhalten, das zur Laufzeit per Bind gemountet wird.

Der Coolify-Weg

Coolify verwandelt den Stack in ein PaaS-Deployment: Es klont das Repository, baut die Images, verdrahtet das interne Netzwerk und stellt Traefik für die TLS-Terminierung davor. Eine dedizierte Compose-Datei automatisiert, was die Standard-Compose dem Betreiber überlässt - Image-Builds, internes Networking, Volumes, Healthchecks und den Mount des Enterprise-Plugins.

Sie erzwingt außerdem die erforderlichen Geheimnisse zum Deploy-Zeitpunkt mit der :?-Syntax: JWT_SECRET, COLLECTOR_SECRET, WIDGET_API_KEY, POSTGRES_PASSWORD, APP_URL und ALLOWED_ORIGINS. Coolify verweigert das Deployment, wenn eines davon fehlt, was eine Klasse halb konfigurierter Deployments in einen sichtbaren Fehler verwandelt, bevor irgendetwas startet.

Die Unterschiede zum Standard-Stack ergeben sich aus der Anwesenheit von Traefik: Die Anwendungsdienste veröffentlichen keine Ports auf dem Host (weisen Sie dem Frontend-Dienst die Domain zu, Port 80), die nginx-Konfiguration ist nur HTTP, weil Traefik TLS terminiert, und der Docker-Socket wird standardmäßig nicht gemountet - er wird nur für den Login-Fluss von Ollama Cloud gebraucht. Ein Schritt-für-Schritt-Runbook deckt die manuellen Schritte ab: rsync des Enterprise-Baums, Ausfüllen der Geheimnisse und Einfügen des Lizenzschlüssels.

Konfiguration: eine .env, gemountet von Compose

Die Container erhalten ihre Konfiguration über die env_file-Angabe von Compose, die auf die .env im Repository-Wurzelverzeichnis zeigt - dieselbe einzige Laufzeit-Konfigurationsdatei, die die Plattform überall verwendet. Der Stack startet mit den reinen Compose-Standards, wenn die Datei fehlt: ausreichend für einen ersten Smoke-Test, nicht für die Produktion.

Zwei Geheimnisse sind strikt erforderlich, und der Server beendet sich ohne sie:

env
# .env im Wurzelverzeichnis - in der Produktion erforderlich
JWT_SECRET=...          # openssl rand -hex 32
COLLECTOR_SECRET=...    # openssl rand -hex 32

ENCRYPTION_KEY und API_KEY_HMAC_SECRET müssen überhaupt nicht vorab bereitgestellt werden: Beim Produktionsstart generiert der Entrypoint des Servers sie einmalig, persistiert sie im Volume server-storage, stellt sie bei jedem Neustart wieder her und bevorzugt einen vom Betreiber gelieferten Wert, wenn er existiert. Das Provisioning generiert nie über vorhandenes Schlüsselmaterial hinweg neu und schlägt laut fehl, bevor es einen Datenbankschritt gibt, damit ein halb geschriebenes Volume nicht still zu einem anderen Schlüssel werden kann.

Es gibt eine spezifische Compose-Falle, die es wert ist, benannt zu werden, weil sie Deployments still degradiert: Fügen Sie den Produktionsdiensten keine Passthrough-Einträge wie LICENSE_KEY=${LICENSE_KEY:-} hinzu. Die Shell-Interpolation löst eine nicht gesetzte Variable zur leeren Zeichenkette auf, und ein explizit leerer environment-Eintrag überschreibt den Wert, der über env_file hereinkommt - ein leer interpolierter LICENSE_KEY verwandelt also ein Enterprise-Deployment in ein Community-Deployment, und ein leer interpolierter ENCRYPTION_KEY überschreibt das Auto-Provisioning des Entrypoints. Lassen Sie diese Schlüssel allein bei der env_file.

Die Details, die in der Produktion zählen

  • Der Collector braucht glibc. Der Collector läuft auf einer Slim-Basis (Debian), nicht Alpine, weil LanceDB, die lokalen Embedding-Transformers und Canvas alle glibc benötigen. Vereinfachen Sie nicht zu Alpine - der Build schlägt fehl.
  • Datenbank-URLs haben drei Formen. Im Compose-Netzwerk spricht der Server mit postgres:5432 (im Compose-Environment verdrahtet); der Standard der Wurzel-.env zeigt auf host.docker.internal für Server auf der Host-Seite; das All-in-One-Image nutzt localhost, weil Postgres im selben Container läuft.
  • Migrationen sind rein additiv. Der Entrypoint des Servers führt bei jedem Start prisma migrate deploy aus, daher ist das Vorwärtsrollen von Images per Politik sicher; ein Rollback bedeutet, ein als gut bekanntes Image neu zu deployen, und ein Code-Rollback stellt angewandte Migrationen nicht wieder her.
  • Skalierung wartet auf das Provisioning. Das Auto-Provisioning der Geheimnisse muss einmal abgeschlossen sein, bevor der Server über eine Replik hinaus skaliert - konkurrierende Replikate beim ersten Start könnten divergierende Schlüssel erzeugen. Die Einzelinstanz ist der dokumentierte Standard; die Multi-Instanz-Skalierung hat ihren eigenen Leitfaden.
  • Gesundheitsendpunkte sind extern. /api/health liefert ein Statusobjekt mit Prüfungen pro Abhängigkeit und eignet sich für externe Uptime-Sonden und Load-Balancer-Prüfungen; strukturierte Logs gehen nach stdout für docker compose logs -f <dienst>.

Veröffentlichte Images werden auf GHCR publiziert (Server, Frontend, Collector, Widget und All-in-One, getaggt latest plus Version), sodass ein Deployment eine Version festnageln kann, statt lokal zu bauen - obwohl docker compose up --build aus dem Repository der vollständig self-hosted Weg bleibt.

Vom Klon zur laufenden Plattform

Der kürzeste Weg, von Anfang bis Ende:

  1. Klonen Sie das Repository und kopieren Sie .env.example nach .env.
  2. Setzen Sie JWT_SECRET und COLLECTOR_SECRET (der Entrypoint provisioniert die Verschlüsselungsschlüssel selbst).
  3. docker compose -f docker/docker-compose.yml up --build -d.
  4. Verfolgen Sie docker compose logs -f server bis Migrationen und Seeding abgeschlossen sind, dann öffnen Sie das Frontend und melden Sie sich an.

Für Air-Gap-Hosts tauschen Sie das All-in-One-Image und den Tarball-Transferfluss ein. Für Coolify importieren Sie das Repository und füllen die erforderlichen Geheimnisse aus - die Plattform erledigt das restliche Deployment.

Lies die vollständige Anleitung in der Doku →

Ähnliche Artikel