Unveränderter Stand von certvia/dev (a48c5fb) plus Craftvia-Spezifikation und Brandbook unter docs/craftvia/. ISMS-Module werden im Folgecommit entfernt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
7.7 KiB
Deployment: Testserver via Coolify (intern)
Zielumgebung: interner Coolify-Host (
192.168.1.207), Docker-Compose-Stack, HTTP über eine*.sslip.io-Domain (kein öffentliches TLS). Ergänztdocs/HANDOVER-DEVOPS.md. Deploy-Datei:docker-compose.coolify.yml.
Architekturüberblick (Coolify)
- Coolify deployt den Compose-Stack aus diesem Repo und übernimmt Reverse-Proxy + Routing — die App wird nicht per Host-Port exponiert.
- Services:
migrate(Init-Job) →app(Next.js standalone) +postgres(pgvector),redis,garage(Objektspeicher) +garage-provision(Init-Job). - Migrationen laufen als eigener Init-Job
migrate(prisma migrate deploy, schlankemigrate-Stage ohnenext build) vorapp— der App-Container migriert selbst nicht. - Objektspeicher Garage (ersetzt MinIO, Community EOL): S3-kompatibel, Single-Node. Buckets/Keys legt NICHT die S3-API an, sondern der Init-Job
garage-provision(Admin-API, idempotent) vorapp. Details/Cutover: eigener Abschnitt unten. - Redis läuft mit
requirepass(F-18):REDIS_PASSWORDals Coolify-Env setzen und in dieREDIS_URLeinsetzen (redis://:<pw>@redis:6379). - Netzsegmentierung (F-18):
postgres/redis/garage/migrateliegen im internen Netz (internal: true, kein Egress); nurappist zusätzlich im default-Netz (Coolify-Proxy). Garage wird nicht per Traefik/Host-Port exponiert. - Secrets/Config kommen aus den Coolify-Env-Variablen (Referenz:
.env.coolify.example), nicht aus einer committeten.env.
1. Gitea mit Coolify verbinden (Deploy Key)
SSH ist erreichbar (git@…sslip.io:22), daher Deploy-Key:
- Coolify → Keys & Tokens → Private Keys: SSH-Key generieren (oder beim Anlegen der Ressource „Private Repository (deploy key)“ automatisch). Öffentlichen Key kopieren.
- Gitea → Repo
msolarczek/ISMS-Tool→ Settings → Deploy Keys → Add Deploy Key: Key einfügen, nur Lesezugriff. - Repo-URL in Coolify (SSH):
git@gitea-vkbhbn2qdkz5ppk9q4qgb0tn.192.168.1.207.sslip.io:msolarczek/ISMS-Tool.git
2. Ressource anlegen
- Projekt/Environment wählen → + New → Private Repository (deploy key).
- Repo
ISMS-Tool, Branchdevops/coolify-testserver(nach erfolgreichem Test →main). - Build Pack: Docker Compose, Compose Location:
docker-compose.coolify.yml.
3. Environment-Variablen
Werte aus .env.coolify.example in Coolify eintragen. Kritisch:
- Hostnamen = Compose-Service-Namen:
postgres,redis,garage(nichtlocalhost);S3_ENDPOINT=http://garage:3900. AUTH_SECRETfrisch:openssl rand -base64 32.AUTH_URL= exakt die in Schritt 4 vergebene App-Domain (http://…).- Garage (siehe Abschnitt unten):
GARAGE_RPC_SECRET+GARAGE_ADMIN_TOKEN(jeopenssl rand -hex 32), sowieS3_ACCESS_KEY=GK+24 Hex (echo "GK$(openssl rand -hex 12)") undS3_SECRET_KEY=openssl rand -hex 32. Alle literal setzen (nicht via${…}— Coolify-Interpolationsfalle).
4. Domain & HTTP
- Beim Service
appunter Domains die*.sslip.io-Domain übernehmen, Schemahttp://. - Dieselbe Domain als
AUTH_URLsetzen (sonst schlägt der NextAuth-Login fehl).
5. Persistenz
Named Volumes müssen als Persistent Storage erkannt sein — v. a. pgdata (sonst Datenverlust bei Redeploy) und garage_meta (Bucket-/Key-/Layout-Definitionen — ohne meta sind die Objektdaten in garage_data nicht adressierbar). Ebenso garage_data, redisdata, backups. garage_meta in die Host-Backup-Strategie aufnehmen (analog pgdata).
6. Deploy
Deploy starten. Reihenfolge: postgres (healthy) → migrate (läuft einmalig durch) → app (startet erst nach erfolgreichem Migrate). Logs von migrate/app bei Fehlern prüfen.
7. Demo-Admin anlegen (einmalig, Testserver)
Der Demo-Seed läuft nicht automatisch. Im Coolify-Terminal des migrate-Containers (builder-Image, hat tsx):
npx tsx prisma/seed.ts
Login danach: admin@demo.example / Demo1234!.
Produktiv: kein Demo-Seed. Stattdessen einmaliger Bootstrap des Plattform-Admins (
provisionTenant({ admin.isPlatformAdmin: true })) — Skript noch zu erstellen (siehedocs/HANDOVER-DEVOPS.md§4).
8. Smoke-Test
Über die App-Domain: Login, Admin-Konsole, eine Modul-Seite.
Redeploy / Migrationen-Nachziehen
Jeder Coolify-Redeploy baut neu und lässt migrate erneut laufen (migrate deploy ist idempotent — nur neue Migrationen werden angewandt). App startet erst nach erfolgreichem Migrate.
Von Test zu Produktiv (Delta)
AUTH_SECRET, DB-Passwörter und die Garage-Secrets (GARAGE_RPC_SECRET,GARAGE_ADMIN_TOKEN,S3_ACCESS_KEY/S3_SECRET_KEY) neu/aus Secret-Store; eigene Domain + gültiges TLS.- Kein Demo-Seed; Erst-Superadmin per Bootstrap-Skript.
- Backups (
pg_dump/Volume-Snapshots inkl.garage_meta) + Restore-Test, Monitoring/Alerting. - Ggf. ≥ 2
app-Replicas hinter dem Coolify-Proxy.
Objektspeicher (Garage) — Cutover, Provisioning & Rollback
Ablösung von MinIO (Community EOL) durch Garage (S3-kompatibel, aktiv gepflegt,
Single-Node). Konzept: docs/KONZEPT-garage-migration.md. Es gibt keine produktiven
Daten → Neu-Deploy statt Datenmigration (kein rclone, kein Wartungsfenster).
Konfig-Bausteine
deploy/garage.toml(eingecheckt, secret-frei):replication_factor=1,s3_region=us-east-1, S3-API:3900, Admin-API:3903.rpc_secret/admin_tokenliest der Daemon aus der Env (GARAGE_RPC_SECRET/GARAGE_ADMIN_TOKEN).- Volumes:
garage_meta(kritisch, ins Host-Backup) +garage_data. - Init-Job
garage-provision(scripts/garage-provision.ts, Admin-API, idempotent): Layout → Bucketisms-documents→ Access-Key importieren (ausS3_ACCESS_KEY/…) → Rechte read/write. Läuft bei jedem Deploy; „already exists“ = Erfolg. OhneGARAGE_ADMIN_TOKENNo-op. Loggt keine Secrets.
Cutover-Runbook (Test-Instanz)
- Compose enthält bereits
garage+garage-provision; der alteminio-Service ist auskommentiert (Rollback-Netz) und wird erst nach Abnahme entfernt. - Coolify-Env literal setzen (nicht
${…}):S3_ENDPOINT=http://garage:3900,S3_REGION=us-east-1,S3_BUCKET=isms-documents,GARAGE_RPC_SECRET,GARAGE_ADMIN_TOKEN,S3_ACCESS_KEY(GK+24 Hex),S3_SECRET_KEY(64 Hex). - Deploy. Reihenfolge:
garage(healthy:/garage status) →garage-provision(legt Layout/Bucket/Key/Rechte an) →app. Optionaler DB-Reset nur, falls alte Objekt-Referenzen stören (bei frischer/geseedeter Instanz unnötig). - Abnahme (§ KONZEPT §9): Upload → Download (
/files/[...key]) → Backup-Export.cvb- Download → DSGVO-ZIP → Restore mit Mandanten-Isolation → Backup-Historie List/ Aufräumen → Negativfall „fehlender Bucket“ = sprechender Fehler.
garage_metain der Host-Backup-Strategie bestätigen. Erst danach denminio-Block und das Volumeminiodataausdocker-compose.coolify.ymlentfernen.
Rollback (unkritisch, keine Prod-Daten): garage/garage-provision wieder aus-,
den auskommentierten minio-Block wieder einkommentieren und die alten S3_*/
MINIO_*-Env setzen; neu deployen. Solange minio + miniodata noch existieren, ist
das ein reiner Compose-/Env-Wechsel.
Provisioning erneut anstoßen / debuggen: Im Coolify-Terminal eines Containers mit
tsx (migrate-Image) npx tsx scripts/garage-provision.ts erneut laufen lassen
(idempotent). „Container weg ohne Logs“ (Coolify entfernt fehlgeschlagene Container
sofort): Provisioning-Ausgabe zusätzlich in eine Datei spiegeln, z. B.
npx tsx scripts/garage-provision.ts 2>&1 | tee /tmp/garage-provision.log.