# Coolify Setup — LeoCRM Production deployment guide for LeoCRM to the Coolify PaaS instance at `server.media-on.de`. ## Architektur LeoCRM läuft als **einzelner docker-compose Stack** in einer Coolify Application. Coolify liest die `docker-compose.yaml` aus dem Git-Repo und startet alle 4 Container (postgres, redis, crm_app, crm_worker) in einem gemeinsamen Stack. ``` Coolify Application (build_pack=dockercompose) ├── postgres (pgvector/pgvector:pg16) ├── redis (redis:7-alpine) ├── crm_app (FastAPI API Server, Port 8000) └── crm_worker (ARQ Background Worker) ``` Alle Container teilen sich ein Docker-Netzwerk. Service-Namen funktionieren als DNS-Namen (z.B. `postgres`, `redis`, `crm_app`, `crm_worker`). **Keine separaten Coolify Services** für DB/Redis/Worker. Das funktioniert nicht, weil Coolify jedem Service ein eigenes Netzwerk gibt und die Container sich nicht per DNS erreichen können. --- ## 1. Voraussetzungen - Coolify server reachable at `https://server.media-on.de`, API token created in *Keys & Tokens → API tokens* (Bearer token, scope: `*`). - DNS **A record** for die App-Domain (z.B. `crm.media-on.de`) zeigt auf die öffentliche IP des Coolify-Servers. - LeoCRM source code in Forgejo repository: `https://forgejo.media-on.de/Leopoldadmin/leocrm.git` (branch `main`). - Ein **Private Deploy Key** in Coolify hinterlegt (für Git-Zugriff). - Python 3.12+ mit `httpx` für das deploy script. --- ## 2. Initial Deployment (automatisiert) ### 2.1 Umgebungsvariablen setzen ```bash export COOLIFY_API_TOKEN="dein-token" export APP_DOMAIN="https://crm.media-on.de" export APP_NAME="leocrm" export DB_PASSWORD="" export REDIS_PASSWORD="" export SECRET_KEY="" export COOLIFY_PROJECT_UUID="" export COOLIFY_SERVER_UUID="" export COOLIFY_PRIVATE_KEY_UUID="" export COOLIFY_ENVIRONMENT="production" # optional export ADMIN_EMAIL="admin@media-on.de" # optional export ADMIN_PASSWORD="Admin123!" # optional ``` ### 2.2 Deploy starten ```bash python scripts/deploy.py --initial ``` Das Script führt einen **2-Phase Deploy** durch: 1. **Phase 1**: Application via `private-deploy-key` erstellen, build_pack auf `dockercompose` setzen, ENV-Variablen setzen, erster Deploy **ohne Domain**. Coolify liest `docker-compose.yaml` aus dem Git-Repo und baut alle Container. 2. **Phase 2**: `docker_compose_domains` setzen (für Traefik-Labels), dann Redeploy. Jetzt ist die App unter der Domain erreichbar. ### 2.3 Warum 2-Phase Deploy? Coolify muss zuerst die `docker-compose.yaml` aus dem Git-Repo lesen, um die Service-Namen zu kennen. Erst dann kann `docker_compose_domains` korrekt zugeordnet werden. Ein Deploy ohne vorherigen Read der Compose-Datei führt zu fehlenden Traefik-Labels → 503 Fehler. --- ## 3. Redeploy (bestehende Anwendung) ```bash export COOLIFY_API_TOKEN="dein-token" export APP_DOMAIN="https://crm.media-on.de" export COOLIFY_APP_UUID="xf7smknlger3hvkrsb910tui" # optional python scripts/deploy.py ``` Triggert `/api/v1/deploy` für die bestehende Coolify Application und wartet auf Erfolg. Danach läuft automatisch die Verifikation (HTTP, Login, Alembic, RLS). --- ## 4. Verifikation ```bash python scripts/deploy.py --verify-only ``` Prüft: - HTTP Health (`/api/v1/health`) - Login (optional, wenn LOGIN_EMAIL/LOGIN_PASSWORD gesetzt) - Alembic Migration Head (via SSH in den postgres Container) - RLS-Tabellen-Anzahl (via SSH) --- ## 5. Wichtige Hinweise ### 5.1 Service-Namen mit Unterstrichen In `docker-compose.yaml` müssen Service-Namen **Unterstriche** verwenden: `crm_app`, `crm_worker` — nicht `crm-app`, `crm-worker`. Coolify konvertiert Bindestriche zu Unterstrichen in `docker_compose_domains`. Bei Bindestrichen in der Compose-Datei gibt es keinen Match → keine Traefik-Labels → 503 Fehler. ### 5.2 docker-compose.yaml (nicht .yml) Coolify sucht nach `docker-compose.yaml` (mit `.yaml`). Eine Datei namens `docker-compose.yml` wird nicht gefunden. ### 5.3 Domain ohne :443 In `docker_compose_domains` darf die Domain **kein** `:443` am Ende haben: ``` ✅ https://crm.media-on.de ❌ https://crm.media-on.de:443 ``` Das `:443` führt zu leeren `Host()` Traefik-Labels. ### 5.4 Kein connect_to_docker_network Innerhalb eines docker-compose Stacks kümmert sich Coolify selbst um das Netzwerk. `connect_to_docker_network=True` ist nicht nötig und sollte nicht gesetzt werden. --- ## 6. Mehrere Instanzen ```bash # Test-Instanz APP_NAME=leocrm-test APP_DOMAIN=https://crm-test.media-on.de \ python scripts/deploy.py --initial # Produktions-Instanz APP_NAME=leocrm APP_DOMAIN=https://crm.media-on.de \ python scripts/deploy.py --initial ``` Jede Instanz hat eigene DB, Redis, Container und Domain. Alle Parameter werden aus `APP_NAME` und `APP_DOMAIN` abgeleitet. --- ## 7. Environment-Variablen in Coolify Das `--initial` Script setzt automatisch folgende ENV-Variablen in Coolify: | Key | Wert | Quelle | |-----|------|--------| | `POSTGRES_USER` | `crm_user` | Default | | `POSTGRES_DB` | `crm_db` | Default | | `DB_PASSWORD` | * | ENV | | `REDIS_PASSWORD` | * | ENV | | `SECRET_KEY` | * | ENV | | `ENVIRONMENT` | `production` | Default | | `LOG_LEVEL` | `INFO` | Default | | `SESSION_COOKIE_SECURE` | `true` | Default | | `STORAGE_PATH` | `/data/storage` | Default | | `CORS_ORIGINS` | APP_DOMAIN | ENV | | `FRONTEND_URL` | APP_DOMAIN | ENV | | `APP_DOMAIN` | APP_DOMAIN | ENV | | `ADMIN_EMAIL` | `admin@media-on.de` | ENV (optional) | | `ADMIN_PASSWORD` | `Admin123!` | ENV (optional) | Die `docker-compose.yaml` verwendet `${VARIABLE}` Syntax — Coolify substituiert aus diesen ENV-Variablen. --- ## 8. Healthcheck In **Coolify → Application → Advanced → Healthcheck**: - **Healthcheck path**: `/api/v1/health` - **Healthcheck method**: `GET` - **Healthcheck interval**: `30s` - **Healthcheck timeout**: `10s` - **Healthcheck retries**: `3` - **Healthcheck start period**: `15s` --- ## 9. Going forward — Redeploys - **Code change** → push to `main` auf Forgejo → `python scripts/deploy.py` (oder Coolify UI → Deployments → Deploy). - **Environment variable change** → Coolify UI (oder API `PATCH .../envs/bulk`) → Deploy (Coolify startet nicht automatisch bei ENV-Änderung neu). - **Domain change** → API (`PATCH /api/v1/applications/{uuid}` mit `docker_compose_domains`) — reproduzierbar, UI als Fallback. --- ## 10. Troubleshooting **503 Fehler (Traefik):** - Domain ohne `:443` in `docker_compose_domains` - Service-Namen mit Unterstrichen in docker-compose.yaml - `docker-compose.yaml` (nicht `.yml`) **Container können sich nicht erreichen (DNS):** - Alles in einem docker-compose Stack (nicht separate Coolify Services) - Kein `connect_to_docker_network` setzen **"Docker Compose file not found":** - Datei heißt `docker-compose.yaml` (nicht `.yml`) **Migration fehlgeschlagen:** ```bash docker exec psql -U crm_user -d crm_db -c "SELECT version_num FROM alembic_version" docker exec alembic upgrade head ``` --- ## 11. Referenzen - Coolify v4 API — `/a0/usr/plugins/coolify_control/help/coolify-control/help.md` - App architecture — `architecture.md` - Deploy script — `scripts/deploy.py` - Install guide — `docs/INSTALL.md`