# Payload CMS Multi-Tenant Infrastructure ## Ü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. ## Architektur ``` ┌─────────────────────────────────────────────────────────────────┐ │ INTERNET │ │ │ │ │ 37.24.237.181 (Public IP) │ │ │ │ │ NAT (Proxmox) │ │ Port 80, 443 │ └────────────────────────────┼────────────────────────────────────┘ │ ┌────────────────────────────┼────────────────────────────────────┐ │ VLAN 181 │ │ 10.10.181.0/24 │ │ │ │ │ ┌───────────────────────┴───────────────────────┐ │ │ │ │ │ │ ▼ ▼ │ │ ┌─────────────────────┐ ┌─────────────────────┐ │ │ │ LXC 700 │ │ LXC 701 │ │ │ │ sv-payload │ │ sv-postgres │ │ │ │ 10.10.181.100 │────────────────▶│ 10.10.181.101 │ │ │ │ │ Port 5432 │ │ │ │ │ - Caddy (80/443) │ │ - PostgreSQL 17 │ │ │ │ - Node.js 22 │ │ │ │ │ │ - Payload CMS │ │ │ │ │ │ - PM2 │ │ │ │ │ └─────────────────────┘ └─────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ ``` ## Server-Details ### LXC 700 - sv-payload (Application Server) | Eigenschaft | Wert | |-------------|------| | IP | 10.10.181.100 | | Öffentlich | 37.24.237.181 (via NAT) | | OS | Debian 13 (Trixie) | | CPU | 4 Cores | | RAM | 4 GB | | Disk | 40 GB | | Domain | pl.c2sgmbh.de | **Installierte Software:** - Node.js 22 LTS (via NodeSource) - pnpm (Package Manager) - Caddy 2.10.2 (Reverse Proxy mit automatischem SSL) - PM2 (Process Manager) - Payload CMS 3.x mit Next.js 15.4.7 **Dienste:** - Caddy läuft als systemd service auf Port 80/443 - Payload läuft via PM2 auf Port 3000 ### LXC 701 - sv-postgres (Database Server) | Eigenschaft | Wert | |-------------|------| | IP | 10.10.181.101 | | Öffentlich | Nein (nur intern) | | OS | Debian 13 (Trixie) | | CPU | 2 Cores | | RAM | 2 GB | | Disk | 20 GB | **Datenbank:** - PostgreSQL 17 - Database: payload_db - User: payload - Passwort: Finden55 - Nur erreichbar von 10.10.181.100 ## Verzeichnisstruktur auf sv-payload ``` /home/payload/ ├── payload-cms/ # Hauptanwendung │ ├── src/ │ │ ├── collections/ │ │ │ ├── Users.ts │ │ │ ├── Media.ts │ │ │ └── Tenants.ts │ │ ├── payload.config.ts │ │ └── payload-types.ts │ ├── .env # Umgebungsvariablen │ ├── ecosystem.config.cjs # PM2 Konfiguration │ ├── package.json │ └── .next/ # Next.js Build Output ├── logs/ │ ├── error-0.log │ └── out-0.log └── ecosystem.config.cjs # PM2 Config (Symlink) ``` ## Konfigurationsdateien ### .env (/home/payload/payload-cms/.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 ``` ### Caddyfile (/etc/caddy/Caddyfile) ```caddyfile { email deine-email@c2sgmbh.de } pl.c2sgmbh.de { reverse_proxy localhost:3000 request_body { max_size 100MB } header { X-Content-Type-Options nosniff X-Frame-Options SAMEORIGIN -Server } encode gzip zstd } ``` ### PM2 Konfiguration (/home/payload/payload-cms/ecosystem.config.cjs) ```javascript module.exports = { apps: [{ name: 'payload', cwd: '/home/payload/payload-cms', script: 'pnpm', args: 'start', env: { NODE_ENV: 'production', PORT: 3000 }, instances: 1, autorestart: true, max_memory_restart: '1G', error_file: '/home/payload/logs/error.log', out_file: '/home/payload/logs/out.log' }] } ``` ## 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 | | Zweitmeinung | zweitmeinung | zweitmein.ng | ### 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) ``` ## Netzwerk & Firewall ### UFW Regeln auf sv-payload ```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 ```bash 22/tcp ALLOW 10.10.181.0/24 # SSH aus VLAN 5432/tcp ALLOW 10.10.181.100 # PostgreSQL nur von Payload ``` ## 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 ## Wichtige Befehle ### Payload Management ```bash # Als payload User su - payload cd ~/payload-cms # Entwicklung pnpm dev # Build für Production pnpm build # Migrationen pnpm payload migrate:create pnpm payload migrate # ImportMap generieren (nach Plugin-Änderungen) pnpm payload generate:importmap ``` ### PM2 Management ```bash pm2 status pm2 logs payload pm2 restart payload pm2 stop payload pm2 start ecosystem.config.cjs ``` ### Caddy Management ```bash sudo systemctl status caddy sudo systemctl restart caddy sudo caddy validate --config /etc/caddy/Caddyfile ``` ### Datenbank ```bash # Von sv-payload aus PGPASSWORD=Finden55 psql -h 10.10.181.101 -U payload -d payload_db # Tabellen anzeigen \dt # Tenants abfragen SELECT * FROM tenants; SELECT * FROM users_tenants; ``` ## Zugriff - **Admin Panel**: https://pl.c2sgmbh.de/admin - **API**: https://pl.c2sgmbh.de/api - **SSH Payload Server**: ssh root@10.10.181.100 (aus VLAN 181) - **SSH Postgres Server**: ssh root@10.10.181.101 (aus VLAN 181)