Zum Inhalt springen

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 16
orimora-redis → Redis 7

AnforderungHinweise
Coolify-ServerDocker-basiert, HTTPS via Let’s Encrypt
Domainsz. B. orimora.com und docs.orimora.com → A-Records auf Server-IP
GitHub-ZugangRepo defcon1702/orimora, Branch main (oder dein Fork)

  1. Coolify-Projekt anlegen, z. B. „Orimora“, Environment „production“.

  2. PostgreSQL 16 im Projekt erstellen (Name: orimora-db). Nach dem Erstellen zeigt Coolify eine interne Verbindungs-URL wie diese:

    postgres://USER:PASSWORD@abc123def456:5432/orimora

    Diese kopieren — du brauchst sie für DATABASE_URL. Der Hostname (abc123def456) ist die UUID der Datenbank, kein menschenlesbarer Name.

  3. Redis 7 erstellen (orimora-redis). Die interne URL kopieren, z. B.:

    redis://default:PASSWORD@xyz789:6379

    Diese für REDIS_URL verwenden.

  4. App-Service erstellen:

    • Quelle: GitHub → defcon1702/orimora, Branch main
    • Build-Pack: Docker Compose
    • Docker Compose Location: /docker-compose.yaml (Standard — passt zum Repo)
    • Kein Base-Directory-Override setzen
  5. Domain zuweisen — nachdem Coolify die Compose-Datei geparst hat, den Service orimora auswählen und https://orimora.com (oder deine Domain) zuweisen.

  6. 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.

  7. App deployen — der erste Deploy baut das Docker-Image, führt die Migrationen beim Start aus und startet den Server auf Port 3000.

  8. 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)
  9. DNS — beide Domains auf die IP deines Coolify-Servers zeigen lassen. Auf die TLS-Zertifikate warten (automatisch via Traefik).

  10. Smoke-Testhttps://orimora.com öffnen, Onboarding abschließen, dann:

    Terminal-Fenster
    curl -s https://orimora.com/api/health

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.

services:
orimora:
build:
context: .
dockerfile: Dockerfile

Baut 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:

VariableWas eintragenErzeugen
DATABASE_URLInterne Postgres-URL aus Schritt 2Aus der Coolify-DB-Ressource kopieren
REDIS_URLInterne Redis-URL aus Schritt 3Aus der Coolify-Redis-Ressource kopieren
SESSION_SECRET64 Hex-Zeichennode -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
MAGIC_LINK_SECRET64 Hex (anderer Wert)Befehl erneut ausführen
APP_URLÖffentliche App-URLhttps://orimora.commuss zur zugewiesenen Domain passen
LLM_ENCRYPTION_KEY64 HexErneut ausführen — verschlüsselt LLM-API-Keys
PUBLISHING_ENCRYPTION_KEY64 HexErneut ausführen — verschlüsselt Webhook-/Publishing-Geheimnisse
App-Einstellungen (optionale Defaults)
Variable im YAMLDefaultBedeutung
NODE_ENVproductionIn Compose fixiert — nicht ändern
PORT3000Interner Container-Port (Traefik routet hierhin)
ALLOW_REGISTRATIONfalseNur während der initialen Einrichtung ggf. auf true
COLLAB_SECRETOptionales gemeinsames Geheimnis für /collab-WebSocket
COLLAB_MAX_CONNECTIONS50Max. gleichzeitige Editoren
CRON_SECRETSchü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
VariableDefaultWarum es zählt
ADDRESS_HEADERX-Forwarded-ForWelcher Header die Client-IP enthält
XFF_DEPTH1Welcher 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
GruppeWichtige Variablen
SMTPSMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD, SMTP_FROM
OAuthGOOGLE_*, MICROSOFT_*, OIDC_*
VAPIDVAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, VAPID_SUBJECT
S3 (reserviert — nicht aktiv)S3_BUCKET, S3_REGION, S3_ACCESS_KEY, S3_SECRET_KEY, S3_ENDPOINT
BackupBACKUP_ENABLED, BACKUP_PATH, BACKUP_RETENTION_*, BACKUP_SCHEDULE, BACKUP_S3_BUCKET
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:3000/']
stop_grace_period: 70s

Coolify 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.

networks:
- coolify
networks:
coolify:
external: true

Die 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/uploads

Bewahrt 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):

Terminal-Fenster
# 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 /v

Danach 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.


Minimum für eine laufende Instanz:

Terminal-Fenster
DATABASE_URL=postgres://USER:PASS@DB_UUID:5432/DATABASE
REDIS_URL=redis://default:PASS@REDIS_UUID:6379
APP_URL=https://orimora.com
SESSION_SECRET=<64 hex>
MAGIC_LINK_SECRET=<64 hex>
LLM_ENCRYPTION_KEY=<64 hex>
PUBLISHING_ENCRYPTION_KEY=<64 hex>
ALLOW_REGISTRATION=false
CRON_SECRET=<Zufallsstring>

Empfohlene Ergänzungen:

Terminal-Fenster
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=...
SMTP_PASSWORD=...
SMTP_FROM=noreply@orimora.com
DOCS_URL=https://docs.orimora.com

Variablen, 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.


EinstellungWert
Build-PackDockerfile
Base Directory/docs
Domainhttps://docs.orimora.com
Port80

Die App leitet /docs auf DOCS_URL um — setze diese ENV-Variable auf dem App-Service passend dazu.


Migrationen laufen automatisch bei jedem Container-Start via run-migrations.mjs. Kein manueller Schritt bei Upgrades.

Zum Debuggen im Container-Terminal:

Terminal-Fenster
node run-migrations.mjs

Jede Migration läuft in einer eigenen Transaktion — ein Fehler bei Migration 15 rollt 1–14 nicht zurück.


Richtige Reihenfolge beim ersten Deploy:

  1. Den Coolify-Service erstellen (Quelle: GitHub, Build-Pack: Docker Compose).
  2. Noch nicht deployen. Zum Tab Environment Variables wechseln.
  3. 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.
  4. ALLOW_REGISTRATION=true temporär setzen (Einrichtung des ersten Users).
  5. Deploy klicken. Der Build läuft, Migrationen werden ausgeführt, die App startet.
  6. Onboarding unter https://orimora.com abschließen, um den Admin-User anzulegen.
  7. ALLOW_REGISTRATION=false wieder 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.


Migrationen laufen automatisch beim Container-Start — kein manuelles yarn db:migrate nötig. Der Upgrade-Ablauf ist:

  1. In Coolify auf Redeploy klicken (oder einen neuen Commit auf main pushen, falls der Webhook konfiguriert ist).
  2. Coolify zieht das neueste Image und startet den neuen Container neben dem alten (Rolling Update).
  3. docker-entrypoint.sh ruft run-migrations.mjs auf, bevor der Server startet — das neue Schema wird angewendet.
  4. 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).


ProblemUrsacheLösung
Container crasht sofortPflicht-:?-Var fehlt (siehe Erster Deploy)Alle sieben Pflicht-Variablen vor dem ersten Deploy setzen
Leere ENV-DuplikateCoolify legt Vars aus ${VAR} automatisch an und du hast sie manuell hinzugefügtLeere Duplikate in der Coolify-UI löschen
EAI_AGAIN / DB-TimeoutApp nicht im coolify-Netzwerk oder falscher HostnameInterne URL aus der Coolify-DB-Ressource verwenden; der Hostname ist die UUID
DB-Hostname geändertPostgres-Ressource neu erstelltDATABASE_URL mit neuer UUID aktualisieren
Boot-Crash, kein klarer FehlerFehlende :?-Pflicht-VarAlle sieben Pflicht-Variablen oben prüfen
Rate-Limit betrifft alleXFF_DEPTH falschDefault 1 für Standard-Coolify beibehalten
Uploads beim Redeploy verlorenVolume nicht gemountetDas 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 nichtCoolify baut bei jedem Push auf mainIn Coolify watch_paths: docs/** setzen, um Rebuilds einzugrenzen

Coolify → App-Service → General → HTTP Basic Auth → aktivieren, Zugangsdaten setzen, neu deployen. Blockiert jeden Zugriff an Traefik, bis du es deaktivierst.


ALLOW_REGISTRATION=false (Default) blockiert die offene Registrierung via Magic Link oder OAuth. Der erste User (Onboarding) und eingeladene User können weiterhin beitreten.


RecordTypWert
orimora.comAServer-IP
docs.orimora.comAServer-IP

Ein Wildcard *.orimora.com vereinfacht künftige Subdomains.