# Payload CMS Multi-Tenant Infrastructure > Letzte Aktualisierung: 09.12.2025 ## Übersicht Diese Dokumentation beschreibt die Infrastruktur eines Payload CMS 3.x Multi-Tenant-Systems für den Betrieb mehrerer Websites unter einer zentralen CMS-Instanz. ## Gesamtarchitektur ``` ┌─────────────────────────────────────────────────────────────────────────────────────┐ │ GESAMTARCHITEKTUR │ │ │ │ ┌───────────────────────────────────────────────────────────────────────────────┐ │ │ │ LOKALE ENTWICKLUNGSUMGEBUNG │ │ │ │ (Proxmox VE Cluster) │ │ │ │ LAN: 10.10.181.0/24 │ │ │ │ │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ │ │ sv-payload │ │ sv-postgres │ │sv-dev-payload│ │sv-analytics │ │ │ │ │ │ LXC 700 │ │ LXC 701 │ │ LXC 702 │ │ LXC 703 │ │ │ │ │ │ Payload CMS │ │ PostgreSQL │ │ Next.js │ │ Umami │ │ │ │ │ │10.10.181.100│ │10.10.181.101│ │10.10.181.102│ │10.10.181.103│ │ │ │ │ │ + Redis │ │ │ │ │ │ │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ └───────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ┌───────────────────┴───────────────────┐ │ │ │ LOKALER INTERNETZUGANG │ │ │ │ 850 Mbps ↓ / 50 Mbps ↑ │ │ │ │ │ │ │ │ Feste IP-Adressen: │ │ │ │ 37.24.237.178 - Router │ │ │ │ 37.24.237.179 - complexcaresolutions │ │ │ │ 37.24.237.180 - Nginx Proxy Manager │ │ │ │ 37.24.237.181 - pl.c2sgmbh.de │ │ │ │ 37.24.237.182 - frei │ │ │ └───────────────────┬───────────────────┘ │ │ │ │ │ INTERNET │ │ │ │ │ ┌──────────────────────────────────┼──────────────────────────────────┐ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐ │ │ │ HETZNER 1 │ │ HETZNER 2 │ │ HETZNER 3 │ │ │ │ CCS GmbH │ │ Martin Porwoll │ │ Backend/Analytics │ │ │ │ │ │ │ │ │ │ │ │ 78.46.87.137 │ │ 94.130.141.114 │ │ 162.55.85.18 │ │ │ │ Debian 12.12 │ │ Ubuntu 24.04 │ │ Debian 13 │ │ │ │ Plesk │ │ Plesk │ │ Native │ │ │ │ │ │ │ │ │ │ │ │ Next.js Frontends │ │ Next.js Frontends │ │ ✅ Payload CMS │ │ │ │ • complexcare... │ │ • porwoll.de │ │ ✅ Umami │ │ │ │ • gunshin.de │ │ • caroline-... │ │ ✅ PostgreSQL 17 │ │ │ └─────────────────────┘ └─────────────────────┘ │ ✅ Redis Cache │ │ │ │ ✅ Claude Code │ │ │ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────────────┘ ``` --- ## Server-Details ### HETZNER 3 - Backend & Analytics (Produktion) | Eigenschaft | Wert | |-------------|------| | **Hostname** | sv-hz03-backend | | **IP-Adresse** | 162.55.85.18 | | **Betriebssystem** | Debian 13 "Trixie" | | **CPU** | AMD Ryzen 5 3600 (6 Cores / 12 Threads) | | **RAM** | 64 GB DDR4 ECC | | **Storage** | 2x 512 GB NVMe SSD (Software RAID 1) | | **Netzwerk** | 1 Gbit/s (garantiert) | | **Traffic** | Unbegrenzt | | **Kosten** | ~€52/Monat | #### Services auf Hetzner 3 | Service | User | Port | URL | Status | |---------|------|------|-----|--------| | PostgreSQL 17 | postgres | 5432 | localhost | ✅ Läuft | | Payload CMS | payload | 3001 | https://cms.c2sgmbh.de | ✅ Läuft | | Umami Analytics | umami | 3000 | https://analytics.c2sgmbh.de | ✅ Läuft | | Redis Cache | redis | 6379 | localhost | ✅ Läuft | | Nginx | root | 80/443 | Reverse Proxy | ✅ Läuft | | Claude Code | claude | - | CLI Tool | ✅ Installiert | #### System-User | User | Zweck | Home-Verzeichnis | |------|-------|------------------| | root | System-Administration | /root | | payload | Payload CMS | /home/payload | | umami | Umami Analytics | /home/umami | | claude | Claude Code / Server-Admin | /home/claude | --- ### HETZNER 1 - Complex Care Solutions GmbH | Eigenschaft | Wert | |-------------|------| | **Eigentümer** | Complex Care Solutions GmbH | | **IP-Adresse** | 78.46.87.137 | | **Betriebssystem** | Debian 12.12 | | **Control Panel** | Plesk Web Pro Edition 18.0.73 | | **CPU** | AMD Ryzen 7 Pro 8700GE | | **RAM** | 64 GB | | **Storage** | 2x 512 GB NVMe SSD (Software RAID 1) | #### Domains auf Hetzner 1 | Domain | Zweck | |--------|-------| | **complexcaresolutions.de** | Hauptdomain | | **gunshin.de** | Portfolio/Holding | | c2sgmbh.de | Kurzform → Redirect | | zweitmeinung-*.de | Fachgebiete → Redirect | --- ### HETZNER 2 - Martin Porwoll (privat) | Eigenschaft | Wert | |-------------|------| | **Eigentümer** | Martin Porwoll (privat) | | **IP-Adresse** | 94.130.141.114 | | **Betriebssystem** | Ubuntu 24.04 LTS | | **Control Panel** | Plesk Web Pro Edition 18.0.73 | | **CPU** | Intel Xeon E3-1275v6 | | **RAM** | 64 GB | | **Storage** | 2x 512 GB NVMe SSD (Software RAID 1) | #### Domains auf Hetzner 2 | Domain | Zweck | |--------|-------| | **porwoll.de** | Hauptdomain | | **caroline-porwoll.de** | Dr. Caroline Porwoll | --- ### Lokale Infrastruktur (Proxmox) - Entwicklung | Server | IP | Port | Funktion | OS | |--------|-----|------|----------|-----| | sv-payload | 10.10.181.100 | 3000 | Payload CMS (Dev) + Redis | Debian 13 | | sv-postgres | 10.10.181.101 | 5432 | PostgreSQL (Dev) | Debian 13 | | sv-dev-payload | 10.10.181.102 | 3001 | Next.js Frontend | Debian 13 | | sv-analytics | 10.10.181.103 | 3000 | Umami (Dev) | Debian 13 | #### Feste IP-Adressen (Lokal → Internet) | IP | Verwendung | |----|------------| | 37.24.237.178 | Router / Gateway | | 37.24.237.179 | complexcaresolutions.cloud | | 37.24.237.180 | Nginx Proxy Manager | | 37.24.237.181 | pl.c2sgmbh.de (Payload Dev) | | 37.24.237.182 | **Frei** | --- ## Credentials ### Produktion (sv-hz03-backend) #### PostgreSQL | Datenbank | User | Passwort | |-----------|------|----------| | payload_db | payload | Suchen55 | | umami_db | umami | Suchen55 | #### Redis ```bash redis-cli -h localhost -p 6379 # Kein Passwort (nur localhost) ``` #### Environment Variables - Payload (.env) ```env DATABASE_URI=postgresql://payload:Suchen55@localhost:5432/payload_db PAYLOAD_SECRET=hxPARlMkmv+ZdCOAMw+N4o2x4mNbERB237iDQTYXALY= PAYLOAD_PUBLIC_SERVER_URL=https://cms.c2sgmbh.de NEXT_PUBLIC_SERVER_URL=https://cms.c2sgmbh.de NODE_ENV=production PORT=3001 REDIS_HOST=localhost REDIS_PORT=6379 ``` ### Entwicklung (pl.c2sgmbh.de) #### PostgreSQL (sv-postgres) | Datenbank | User | Passwort | |-----------|------|----------| | payload_db | payload | Finden55 | #### Environment Variables (.env) ```env DATABASE_URI=postgresql://payload:Finden55@10.10.181.101:5432/payload_db PAYLOAD_SECRET=a53b254070d3fffd2b5cfcc3 PAYLOAD_PUBLIC_SERVER_URL=https://pl.c2sgmbh.de NEXT_PUBLIC_SERVER_URL=https://pl.c2sgmbh.de NODE_ENV=production PORT=3000 REDIS_HOST=localhost REDIS_PORT=6379 ``` --- ## Multi-Tenant Konzept Das System verwendet `@payloadcms/plugin-multi-tenant` für die Mandantenfähigkeit. ### Tenants (Mandanten) Jeder Tenant repräsentiert eine separate Website: | Tenant | Slug | Domains | |--------|------|---------| | porwoll.de | porwoll | porwoll.de, www.porwoll.de | | Complex Care Solutions GmbH | c2s | complexcaresolutions.de | | Gunshin | gunshin | gunshin.de | ### Datenisolation - Jeder Content (Media, Pages, Posts etc.) gehört zu genau einem Tenant - User werden Tenants zugewiesen und sehen nur deren Inhalte - Die Domain-Erkennung erfolgt automatisch durch das Plugin ### Datenbank-Tabellen ``` tenants - Mandanten-Stammdaten tenants_domains - Domain-Zuordnungen users_tenants - User-Mandanten-Beziehung (N:M) ``` --- ## Redis Caching ### Architektur ``` ┌─────────────────────────────────────────────────────────────────────────────────────┐ │ REDIS CACHING STRATEGIE │ │ │ │ Request → Payload CMS → Redis Cache? │ │ │ │ │ ┌────┴────┐ │ │ HIT MISS │ │ │ │ │ │ ▼ ▼ │ │ Return PostgreSQL → Cache in Redis → Return │ │ │ │ Cache-Typen: │ │ • API Response Cache (GET /api/pages, /api/posts) │ │ • Automatische Invalidierung bei Content-Änderungen │ │ │ │ Konfiguration: │ │ • Max Memory: 2GB (Prod) / 512MB (Dev) │ │ • Eviction: allkeys-lru │ │ • TTL: 5 Minuten (Standard) │ │ │ └─────────────────────────────────────────────────────────────────────────────────────┘ ``` ### Redis Befehle ```bash # Status prüfen redis-cli ping # Statistiken redis-cli info stats # Cache-Keys anzeigen redis-cli keys "*" # Cache leeren redis-cli flushdb # Live-Monitoring redis-cli monitor ``` --- ## Deployment Workflow ``` ┌─────────────────────────────────────────────────────────────────────────────────────┐ │ DEPLOYMENT WORKFLOW │ │ │ │ ┌──────────────────────────────────┐ ┌──────────────────────────────────┐ │ │ │ ENTWICKLUNG (DEV) │ │ PRODUKTION (PROD) │ │ │ │ pl.c2sgmbh.de │ │ cms.c2sgmbh.de │ │ │ │ 37.24.237.181 │ │ 162.55.85.18 │ │ │ └──────────────────────────────────┘ └──────────────────────────────────┘ │ │ │ │ Step 1: CODE ENTWICKELN │ │ ┌────────────────────────────────────────────────────────────────────────┐ │ │ │ cd /home/payload/payload-cms │ │ │ │ pnpm dev # Lokal testen │ │ │ │ pnpm build # Build-Test │ │ │ │ pm2 restart payload # Auf Dev-Server deployen │ │ │ └────────────────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ │ │ Step 2: ZU GITHUB PUSHEN │ │ ┌────────────────────────────────────────────────────────────────────────┐ │ │ │ git add . # Alle Änderungen stagen │ │ │ │ git commit -m "feat: XYZ" # Commit erstellen │ │ │ │ git push origin main # Zu GitHub pushen │ │ │ └────────────────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ GITHUB REPOSITORY (PRIVAT) │ │ │ │ https://github.com/c2s-admin/cms.c2sgmbh │ │ │ └────────────────────────────────────────────────────┘ │ │ ↓ │ │ │ │ Step 3: AUF PRODUKTION DEPLOYEN │ │ ┌────────────────────────────────────────────────────────────────────────┐ │ │ │ ssh payload@162.55.85.18 │ │ │ │ ~/deploy.sh # Automatisches Deployment │ │ │ │ │ │ │ │ Das deploy.sh Script macht: │ │ │ │ ├─ git pull origin main # Code von GitHub holen │ │ │ │ ├─ pnpm install # Dependencies aktualisieren │ │ │ │ ├─ pnpm build # Produktions-Build │ │ │ │ └─ pm2 restart payload # Service neustarten │ │ │ └────────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────────────┘ ``` ### Git-Setup auf Servern | Server | User | Remote | Auth-Methode | Status | |--------|------|--------|--------------|--------| | pl.c2sgmbh.de (Dev) | payload | HTTPS | GitHub CLI (`gh auth`) | ✅ Konfiguriert | | cms.c2sgmbh.de (Prod) | payload | SSH | SSH-Key | ✅ Eingerichtet | ### Deployment-Befehle **Entwicklungsserver → GitHub:** ```bash cd /home/payload/payload-cms git status pnpm build pm2 restart payload git add . git commit -m "feat: Beschreibung der Änderung" git push origin main ``` **GitHub → Produktionsserver:** ```bash # Option A: SSH + Deploy-Script (empfohlen) ssh payload@162.55.85.18 '~/deploy.sh' # Option B: Manuelles SSH-Login ssh payload@162.55.85.18 cd ~/payload-cms git pull origin main pnpm install pnpm build pm2 restart payload ``` --- ## Backup ### Backup-Script (~/backup.sh) ```bash #!/bin/bash set -e BACKUP_DIR=~/backups DATE=$(date +%Y-%m-%d_%H-%M-%S) RETENTION_DAYS=7 mkdir -p $BACKUP_DIR # PostgreSQL Backup PGPASSWORD=Suchen55 pg_dump -h localhost -U payload payload_db > $BACKUP_DIR/payload_db_$DATE.sql gzip $BACKUP_DIR/payload_db_$DATE.sql # Alte Backups löschen find $BACKUP_DIR -name "*.sql.gz" -mtime +$RETENTION_DAYS -delete ``` ### Cronjob (täglich 3:00 Uhr) ``` 0 3 * * * /home/payload/backup.sh >> /home/payload/backups/backup.log 2>&1 ``` --- ## Service-Management ### PM2 Befehle ```bash pm2 status # Status pm2 logs payload # Logs pm2 restart payload # Neustart pm2 save # Autostart speichern ``` ### Systemd Services ```bash # PostgreSQL systemctl status postgresql systemctl restart postgresql # Nginx systemctl status nginx systemctl restart nginx nginx -t # Config testen # Redis systemctl status redis-server systemctl restart redis-server ``` --- ## URLs Übersicht | Service | Entwicklung | Produktion | |---------|-------------|------------| | Payload Admin | https://pl.c2sgmbh.de/admin | https://cms.c2sgmbh.de/admin | | Payload API | https://pl.c2sgmbh.de/api | https://cms.c2sgmbh.de/api | | Umami | - | https://analytics.c2sgmbh.de | --- ## SSH Schnellzugriff ```bash # Produktion (Hetzner 3) ssh root@162.55.85.18 # Root ssh payload@162.55.85.18 # Payload User ssh umami@162.55.85.18 # Umami User ssh claude@162.55.85.18 # Claude Code # Hetzner Server ssh root@78.46.87.137 # Hetzner 1 (CCS) ssh root@94.130.141.114 # Hetzner 2 (Porwoll) # Entwicklung (Proxmox) ssh payload@10.10.181.100 # sv-payload ssh root@10.10.181.101 # sv-postgres ``` --- ## Netzwerk & Firewall ### UFW Regeln auf sv-payload (Dev) ```bash 22/tcp ALLOW 10.10.181.0/24 # SSH aus VLAN 80/tcp ALLOW Anywhere # HTTP (ACME) 443/tcp ALLOW Anywhere # HTTPS ``` ### UFW Regeln auf sv-postgres (Dev) ```bash 22/tcp ALLOW 10.10.181.0/24 # SSH aus VLAN 5432/tcp ALLOW 10.10.181.100 # PostgreSQL nur von Payload ``` ### UFW Regeln auf sv-hz03-backend (Prod) ```bash 22/tcp ALLOW Anywhere # SSH 80/tcp ALLOW Anywhere # HTTP 443/tcp ALLOW Anywhere # HTTPS ``` --- ## SSL Zertifikate | Domain | Anbieter | Status | |--------|----------|--------| | pl.c2sgmbh.de | Let's Encrypt (Caddy) | Auto-Renewal | | cms.c2sgmbh.de | Let's Encrypt (Certbot) | Auto-Renewal | | analytics.c2sgmbh.de | Let's Encrypt (Certbot) | Auto-Renewal | --- ## Tech Stack | Komponente | Technologie | Version | |------------|-------------|---------| | CMS | Payload CMS | 3.x | | Framework | Next.js | 15.4.7 | | Runtime | Node.js | 22.x | | Datenbank | PostgreSQL | 17 | | Cache | Redis | 7.x | | Analytics | Umami | 3.x | | Process Manager | PM2 | Latest | | Package Manager | pnpm | Latest | | Reverse Proxy (Dev) | Caddy | 2.10.2 | | Reverse Proxy (Prod) | Nginx | Latest | | SSL | Let's Encrypt | - | --- ## DSGVO-Konformität Die Architektur wurde bewusst ohne Cloudflare designed: - Keine US-Dienste im Datenpfad für Admin-Zugriffe - Direkte öffentliche IP statt Proxy - Keine Auftragsverarbeiter-Verträge für CDN nötig - Redakteur-IPs und Sessions bleiben in DE --- ## Checkliste nach Deployment - [ ] `pm2 status` - Alle Prozesse online? - [ ] `redis-cli ping` - Redis antwortet? - [ ] Admin Panel erreichbar? - [ ] `pm2 logs payload --lines 10` - Keine Fehler? --- *Stand: 09. Dezember 2025*