Coolify Setup
Diese Anleitung führt dich Schritt für Schritt durch das Deployment von Orimora auf Coolify — einer selbst gehosteten PaaS mit eingebautem Traefik, TLS und Datenbank-Verwaltung. Kein Kubernetes-Wissen nötig.
Orimora läuft als getrennte Coolify-Services (App, Docs, Datenbank, Redis). Jeder kann unabhängig aktualisiert werden.
orimora.com → orimora-app (SvelteKit — docker-compose.yaml)docs.orimora.com → orimora-docs (Starlight Static Site — /docs Dockerfile)Nur intern (keine öffentliche Domain):
orimora-db → PostgreSQL 16orimora-redis → Redis 7Bevor du startest
Abschnitt betitelt „Bevor du startest“| Anforderung | Hinweise |
|---|---|
| Coolify-Server | Docker-basiert, HTTPS via Let’s Encrypt |
| Domains | z. B. orimora.com und docs.orimora.com → A-Records auf Server-IP |
| GitHub-Zugang | Repo defcon1702/orimora, Branch main (oder dein Fork) |
Schritt-für-Schritt-Einrichtung
Abschnitt betitelt „Schritt-für-Schritt-Einrichtung“-
Coolify-Projekt anlegen, z. B. „Orimora“, Environment „production“.
-
PostgreSQL 16 im Projekt erstellen (Name:
orimora-db). Nach dem Erstellen zeigt Coolify eine interne Verbindungs-URL wie diese:postgres://USER:PASSWORD@abc123def456:5432/orimoraDiese kopieren — du brauchst sie für
DATABASE_URL. Der Hostname (abc123def456) ist die UUID der Datenbank, kein menschenlesbarer Name. -
Redis 7 erstellen (
orimora-redis). Die interne URL kopieren, z. B.:redis://default:PASSWORD@xyz789:6379Diese für
REDIS_URLverwenden. -
App-Service erstellen:
- Quelle: GitHub →
defcon1702/orimora, Branchmain - Build-Pack: Docker Compose
- Docker Compose Location:
/docker-compose.yaml(Standard — passt zum Repo) - Kein Base-Directory-Override setzen
- Quelle: GitHub →
-
Domain zuweisen — nachdem Coolify die Compose-Datei geparst hat, den Service
orimoraauswählen undhttps://orimora.com(oder deine Domain) zuweisen. -
Umgebungsvariablen ausfüllen — Coolify erkennt automatisch jede
${VAR}aus der Compose-Datei. Mit:?im YAML markierte Variablen erscheinen als Pflicht (roter Rand). Siehe die Tabellen unten. -
App deployen — der erste Deploy baut das Docker-Image, führt die Migrationen beim Start aus und startet den Server auf Port 3000.
-
Docs-Service erstellen (optional, aber empfohlen):
- Gleiches Repo und Branch
- Build-Pack: Dockerfile
- Base Directory:
/docs - Domain:
https://docs.orimora.com - Port:
80 - Env:
PUBLIC_SITE_URL=https://docs.orimora.com(falls angezeigt)
-
DNS — beide Domains auf die IP deines Coolify-Servers zeigen lassen. Auf die TLS-Zertifikate warten (automatisch via Traefik).
-
Smoke-Test —
https://orimora.comöffnen, Onboarding abschließen, dann:Terminal-Fenster curl -s https://orimora.com/api/health
docker-compose.yaml verstehen
Abschnitt betitelt „docker-compose.yaml verstehen“Coolify nutzt die Datei im Repo-Root. Hier ist, was jeder Abschnitt tut — du musst diese Datei in Coolify nicht bearbeiten; konfiguriere alles über die UI-Umgebungsvariablen.
Service: orimora
Abschnitt betitelt „Service: orimora“services: orimora: build: context: . dockerfile: DockerfileBaut das SvelteKit-Produktions-Image aus dem Dockerfile des Repos. Migrationen laufen automatisch in docker-entrypoint.sh, bevor der Server startet.
Pflicht-Umgebung (:?) — 7 Variablen
Diese müssen in Coolify gesetzt sein, sonst scheitert der Container beim Start:
| Variable | Was eintragen | Erzeugen |
|---|---|---|
DATABASE_URL | Interne Postgres-URL aus Schritt 2 | Aus der Coolify-DB-Ressource kopieren |
REDIS_URL | Interne Redis-URL aus Schritt 3 | Aus der Coolify-Redis-Ressource kopieren |
SESSION_SECRET | 64 Hex-Zeichen | node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" |
MAGIC_LINK_SECRET | 64 Hex (anderer Wert) | Befehl erneut ausführen |
APP_URL | Öffentliche App-URL | https://orimora.com — muss zur zugewiesenen Domain passen |
LLM_ENCRYPTION_KEY | 64 Hex | Erneut ausführen — verschlüsselt LLM-API-Keys |
PUBLISHING_ENCRYPTION_KEY | 64 Hex | Erneut ausführen — verschlüsselt Webhook-/Publishing-Geheimnisse |
App-Einstellungen (optionale Defaults)
| Variable im YAML | Default | Bedeutung |
|---|---|---|
NODE_ENV | production | In Compose fixiert — nicht ändern |
PORT | 3000 | Interner Container-Port (Traefik routet hierhin) |
ALLOW_REGISTRATION | false | Nur während der initialen Einrichtung ggf. auf true |
COLLAB_SECRET | — | Optionales gemeinsames Geheimnis für /collab-WebSocket |
COLLAB_MAX_CONNECTIONS | 50 | Max. gleichzeitige Editoren |
CRON_SECRET | — | Schützt den Cleanup-Cron-Endpunkt — Zufallsstring erzeugen |
Reverse Proxy / Rate Limiting (wichtig auf Coolify)
Abschnitt betitelt „Reverse Proxy / Rate Limiting (wichtig auf Coolify)“- ADDRESS_HEADER=${ADDRESS_HEADER:-X-Forwarded-For}- XFF_DEPTH=${XFF_DEPTH:-1}Coolify terminiert TLS an Traefik und leitet die echte Client-IP via X-Forwarded-For weiter. Diese Defaults sind für Standard-Coolify korrekt — belasse sie, es sei denn, du fügst eine weitere Proxy-Ebene hinzu.
Reverse Proxy / Rate Limiting
| Variable | Default | Warum es zählt |
|---|---|---|
ADDRESS_HEADER | X-Forwarded-For | Welcher Header die Client-IP enthält |
XFF_DEPTH | 1 | Welcher IP in der Kette vertrauen (1 = direkter Upstream) |
Ohne diese Werte kollabieren die Login-Rate-Limits zu einem einzigen Bucket für alle Nutzer hinter Traefik.
SMTP, OAuth, VAPID, S3, Backup
| Gruppe | Wichtige Variablen |
|---|---|
| SMTP | SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD, SMTP_FROM |
| OAuth | GOOGLE_*, MICROSOFT_*, OIDC_* |
| VAPID | VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, VAPID_SUBJECT |
| S3 (reserviert — nicht aktiv) | S3_BUCKET, S3_REGION, S3_ACCESS_KEY, S3_SECRET_KEY, S3_ENDPOINT |
| Backup | BACKUP_ENABLED, BACKUP_PATH, BACKUP_RETENTION_*, BACKUP_SCHEDULE, BACKUP_S3_BUCKET |
Health Check & Graceful Shutdown
Abschnitt betitelt „Health Check & Graceful Shutdown“healthcheck: test: ['CMD', 'curl', '-f', 'http://localhost:3000/']stop_grace_period: 70sCoolify wartet, bis die App auf / antwortet, bevor sie als „healthy“ markiert wird. Beim Redeploy wartet Docker bis zu 70 Sekunden, damit laufende Jobs (E-Mails, Webhooks) abgeschlossen werden.
Netzwerk
Abschnitt betitelt „Netzwerk“ networks: - coolify
networks: coolify: external: trueDie App muss dem externen coolify-Netzwerk von Coolify beitreten, um die verwalteten Postgres- und Redis-Instanzen über den UUID-Hostnamen zu erreichen. Das ist vorkonfiguriert — nicht entfernen.
volumes: - orimora-uploads:/app/uploadsBewahrt hochgeladene Dateien (Bild-Anhänge, Avatare, Logos) über Redeploys hinweg auf.
Volume-Ownership — Bild-/Avatar-Upload schlägt mit EACCES fehl
Abschnitt betitelt „Volume-Ownership — Bild-/Avatar-Upload schlägt mit EACCES fehl“Der Container läuft non-root (USER node, UID 1000) und kann ein root-owned Volume zur
Laufzeit nicht chownen. Das Image liefert /app/uploads als node-owned aus, also erbt ein
frisches, leeres Named Volume diese Ownership und Uploads funktionieren einfach. Aber ein Volume,
das bevor dieses Verzeichnis im Image existierte angelegt wurde (älterer Deploy) oder das
Coolify als root vorab erstellt hat, bleibt root-owned — dann scheitern upload_image (MCP) und
Attachment-Uploads mit EACCES: permission denied, open '/app/uploads/attachments/…'.
Fix (einmalig, vom Docker-Host oder einem Coolify-Terminal):
# orimora-uploads ggf. durch den tatsächlichen Volume-Namen ersetzen, falls Coolify ihn präfixiert hat.docker run --rm -v orimora-uploads:/v alpine chown -R 1000:1000 /vDanach neu deployen. Bei einem Bind-Mount statt eines Named Volume das Host-Verzeichnis
chown -R 1000:1000. Prüfen mit docker run --rm -v orimora-uploads:/v alpine ls -lan /v — die
Einträge sollten der UID 1000 gehören.
Copy-Paste-Checkliste für die Coolify-ENV-UI
Abschnitt betitelt „Copy-Paste-Checkliste für die Coolify-ENV-UI“Minimum für eine laufende Instanz:
DATABASE_URL=postgres://USER:PASS@DB_UUID:5432/DATABASEREDIS_URL=redis://default:PASS@REDIS_UUID:6379APP_URL=https://orimora.comSESSION_SECRET=<64 hex>MAGIC_LINK_SECRET=<64 hex>LLM_ENCRYPTION_KEY=<64 hex>PUBLISHING_ENCRYPTION_KEY=<64 hex>ALLOW_REGISTRATION=falseCRON_SECRET=<Zufallsstring>Empfohlene Ergänzungen:
SMTP_HOST=smtp.example.comSMTP_PORT=587SMTP_USER=...SMTP_PASSWORD=...SMTP_FROM=noreply@orimora.comDOCS_URL=https://docs.orimora.comVariablen, die nicht in docker-compose.yaml stehen, aber von der App genutzt werden (in Coolify setzen, falls nötig): EXTRA_ALLOWED_ORIGINS, API_KEY_SECRET, Webhook-/Publishing-Tuning — siehe Konfiguration.
Docs-Service (orimora-docs)
Abschnitt betitelt „Docs-Service (orimora-docs)“| Einstellung | Wert |
|---|---|
| Build-Pack | Dockerfile |
| Base Directory | /docs |
| Domain | https://docs.orimora.com |
| Port | 80 |
Die App leitet /docs auf DOCS_URL um — setze diese ENV-Variable auf dem App-Service passend dazu.
Datenbank-Migrationen
Abschnitt betitelt „Datenbank-Migrationen“Migrationen laufen automatisch bei jedem Container-Start via run-migrations.mjs. Kein manueller Schritt bei Upgrades.
Zum Debuggen im Container-Terminal:
node run-migrations.mjsJede Migration läuft in einer eigenen Transaktion — ein Fehler bei Migration 15 rollt 1–14 nicht zurück.
Ablauf des ersten Deploys
Abschnitt betitelt „Ablauf des ersten Deploys“Richtige Reihenfolge beim ersten Deploy:
- Den Coolify-Service erstellen (Quelle: GitHub, Build-Pack: Docker Compose).
- Noch nicht deployen. Zum Tab Environment Variables wechseln.
- Alle sieben Pflicht-Variablen ausfüllen (siehe Tabelle oben) — mindestens
DATABASE_URL,REDIS_URL,APP_URL,SESSION_SECRET,MAGIC_LINK_SECRET,LLM_ENCRYPTION_KEY,PUBLISHING_ENCRYPTION_KEY. ALLOW_REGISTRATION=truetemporär setzen (Einrichtung des ersten Users).- Deploy klicken. Der Build läuft, Migrationen werden ausgeführt, die App startet.
- Onboarding unter
https://orimora.comabschließen, um den Admin-User anzulegen. ALLOW_REGISTRATION=falsewieder setzen und neu deployen (oder gemäß deiner Policy belassen).
Crasht der Container beim ersten Deploy trotzdem, öffne Coolify → Service → Logs und suche nach Missing required environment variable. Der Fehler nennt die genaue Variable.
Orimora upgraden
Abschnitt betitelt „Orimora upgraden“Migrationen laufen automatisch beim Container-Start — kein manuelles yarn db:migrate nötig. Der Upgrade-Ablauf ist:
- In Coolify auf Redeploy klicken (oder einen neuen Commit auf
mainpushen, falls der Webhook konfiguriert ist). - Coolify zieht das neueste Image und startet den neuen Container neben dem alten (Rolling Update).
docker-entrypoint.shruftrun-migrations.mjsauf, bevor der Server startet — das neue Schema wird angewendet.- Der alte Container wird entfernt, sobald der Health-Check beim neuen grün ist.
Rollback: Falls eine Migration Probleme verursacht, über Coolify → Service → Deployments auf das vorherige Image zurückrollen. Beachte, dass Schema-Rollbacks ein manuelles SQL-Revert erfordern — Migrationen sind nicht automatisch reversibel. Vor Major-Version-Upgrades immer einen DB-Snapshot erstellen (Coolify-Datenbank-Backup oder pg_dump).
Bekannte Fallstricke
Abschnitt betitelt „Bekannte Fallstricke“| Problem | Ursache | Lösung |
|---|---|---|
| Container crasht sofort | Pflicht-:?-Var fehlt (siehe Erster Deploy) | Alle sieben Pflicht-Variablen vor dem ersten Deploy setzen |
| Leere ENV-Duplikate | Coolify legt Vars aus ${VAR} automatisch an und du hast sie manuell hinzugefügt | Leere Duplikate in der Coolify-UI löschen |
EAI_AGAIN / DB-Timeout | App nicht im coolify-Netzwerk oder falscher Hostname | Interne URL aus der Coolify-DB-Ressource verwenden; der Hostname ist die UUID |
| DB-Hostname geändert | Postgres-Ressource neu erstellt | DATABASE_URL mit neuer UUID aktualisieren |
| Boot-Crash, kein klarer Fehler | Fehlende :?-Pflicht-Var | Alle sieben Pflicht-Variablen oben prüfen |
| Rate-Limit betrifft alle | XFF_DEPTH falsch | Default 1 für Standard-Coolify beibehalten |
| Uploads beim Redeploy verloren | Volume nicht gemountet | Das orimora-uploads-Volume nicht entfernen |
Bild-Upload schlägt fehl (EACCES) | orimora-uploads-Volume ist root-owned (nicht UID 1000) | docker run --rm -v orimora-uploads:/v alpine chown -R 1000:1000 /v (Details) |
| Webhook triggert bei Docs-Push nicht | Coolify baut bei jedem Push auf main | In Coolify watch_paths: docs/** setzen, um Rebuilds einzugrenzen |
Wartungsmodus (Basic Auth)
Abschnitt betitelt „Wartungsmodus (Basic Auth)“Coolify → App-Service → General → HTTP Basic Auth → aktivieren, Zugangsdaten setzen, neu deployen. Blockiert jeden Zugriff an Traefik, bis du es deaktivierst.
Registrierungs-Sperre
Abschnitt betitelt „Registrierungs-Sperre“ALLOW_REGISTRATION=false (Default) blockiert die offene Registrierung via Magic Link oder OAuth. Der erste User (Onboarding) und eingeladene User können weiterhin beitreten.
DNS-Referenz
Abschnitt betitelt „DNS-Referenz“| Record | Typ | Wert |
|---|---|---|
orimora.com | A | Server-IP |
docs.orimora.com | A | Server-IP |
Ein Wildcard *.orimora.com vereinfacht künftige Subdomains.
Weiterführend
Abschnitt betitelt „Weiterführend“- Installation — lokale Entwicklung
- Konfiguration — vollständige ENV-Referenz inkl. Vars, die nicht in Compose stehen
- Datenbank-Migrationen — wie der Runner funktioniert
- Interne Ops-Referenz:
deploy/coolify-traefik.md