cms.c2sgmbh/docs/INFRASTRUCTURE.md
Martin Porwoll 6ccb50c5f4 docs: consolidate and update documentation
- Remove obsolete instruction documents (PROMPT_*.md, SECURITY_FIXES.md)
- Update CLAUDE.md with security features, test suite, audit logs
- Merge Techstack_Dokumentation into INFRASTRUCTURE.md
- Update SECURITY.md with custom login route documentation
- Add changelog to TODO.md
- Update email service and data masking for SMTP error handling
- Extend test coverage for CSRF and data masking

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-09 09:25:00 +00:00

24 KiB

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

redis-cli -h localhost -p 6379
# Kein Passwort (nur localhost)

Environment Variables - Payload (.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)

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

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

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:

# 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)

#!/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

pm2 status              # Status
pm2 logs payload        # Logs
pm2 restart payload     # Neustart
pm2 save                # Autostart speichern

Systemd Services

# 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

# 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)

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)

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)

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