Ein mandantenfähiger Entwicklungsstack mit Docker: Vollständiges Setup für skalierbare Kunden-Deployments
So erstellen Sie eine vorlagenbasierte, mandantenfähige Entwicklungsumgebung mit 16 containerisierten Diensten, die offline funktioniert und dennoch online über subdomainbasiertes Routing erreichbar bleibt.
Die Verwaltung von Entwicklungsumgebungen für mehrere Kunden bedeutet oft die Wahl zwischen komplexen manuellen Setups oder teuren Cloud-Lösungen. Manuelle Bereitstellungen sind zeitaufwändig und fehleranfällig. Cloud-Plattformen sind bequem, erzeugen aber Vendor-Lock-in und laufende Kosten, die mit der Nutzung skalieren.
Heute führen wir Sie durch den Aufbau eines skalierbaren, mandantenfähigen Entwicklungsstacks, der Ihnen beides bietet: starke Trennung der Kundenumgebungen mit automatisierten Deployments und vollständige Kontrolle über Ihre Infrastruktur. Dieser Ansatz baut auf unserer Philosophie selbst betriebener Lösungen auf – ähnlich wie wir gezeigt haben, dass Sie n8n für die Workflow-Automatisierung selbst betreiben und Windmill mit Docker bereitstellen können, um die vollständige Kontrolle über den Betrieb zu behalten.
Die von uns verwendeten Tools
Sehen wir uns zunächst an, was jede Komponente unserer umfassenden 16-Container-Architektur leistet:
Docker: Die Grundlage Ihrer Containerisierung
Docker bietet die Trennung und Konsistenz, die wir für mandantenfähige Umgebungen benötigen. Jeder Kunde erhält eigene Container mit identischen Konfigurationen. Das trägt dazu bei, sicherzustellen, dass das, was in der Entwicklung funktioniert, auch im Produktivbetrieb funktioniert. Stellen Sie sich mehrere getrennte Server auf derselben Hardware vor, die sich allerdings weiterhin CPU, Arbeitsspeicher, Festplatte und Kernel teilen.
Der entscheidende Vorteil ist die starke Trennung zwischen Kunden. Daten, Konfigurationen und Anpassungen jedes Kunden liegen in getrennten Containern, Netzwerken und Volumes, sodass sie die anderer Kunden nicht beeinträchtigen. Das ist wichtig, wenn Sie mehrere Geschäftskunden mit unterschiedlichen Anforderungen und Sicherheitsbedürfnissen betreuen.
Traefik: Intelligenter Reverse Proxy und Load Balancer
Traefik fungiert als intelligenter Verkehrsleiter, der Anfragen automatisch basierend auf Domainnamen an die korrekte Kundenumgebung weiterleitet. Anstatt manuell komplexe Apache- oder Nginx-Regeln zu konfigurieren, liest Traefik Labels aus Ihren Docker-Containern und richtet das Routing automatisch ein.
Stellen Sie sich Traefik als einen intelligenten Empfang vor, der genau weiß, in welches Büro (Container) jeder Besucher (Anfrage) gehört, ohne dass Sie jedes Mal den Weg erklären müssen. In unserem Setup übernimmt Traefik die SSL-Terminierung und automatische Diensterkennung und bietet detaillierte Monitoring-Dashboards.
Cloudflare Tunnels: Sicherer externer Zugriff
Cloudflare Tunnels bieten sicheren Zugriff auf Ihren lokalen Entwicklungsstack ohne komplexe Firewall-Konfigurationen oder VPNs. Jede Kundendomain erhält ihren eigenen Tunnel, wodurch die externen Zugriffswege der einzelnen Kunden getrennt bleiben.
Der Vorteil: Ihre Entwicklungsumgebungen bleiben lokal und sicher, während Kunden mit entsprechender Authentifizierung von überall auf ihre jeweiligen Dienste zugreifen können – ähnlich wie beim sicheren externen Zugriff in unserem n8n-Hosting-Leitfaden.
Der vollständige Stack: Alles, was Ihre Kunden brauchen
Unser mandantenfähiger Stack umfasst vier Kategorien zentraler Dienste, verteilt auf 16 Container pro Kunde:
Workflow-Automatisierung und Geschäftslogik:
- n8n: Vollständige Workflow-Automatisierungsplattform für die Automatisierung von Geschäftsprozessen
- Authentik: Single-Sign-On und Identitätsmanagement auf Unternehmensniveau (3 Container: Server, Worker, Redis-Cache)
Datenbank- und Backend-Dienste:
- PostgreSQL: Robustes Datenbank-Backend, das alle Dienste mit optimiertem Connection Pooling unterstützt
- Supabase-Stack: Vollständiges Backend-as-a-Service mit 6 spezialisierten Containern (Studio, Meta, Auth, REST API, Realtime, Kong Gateway)
- NocoDB: No-Code-Datenbankoberfläche für die Kundendatenverwaltung
KI und Intelligenz:
- Ollama: Lokale KI-Sprachmodelle mit GPU-Beschleunigung für intelligente Automatisierung
- Qdrant (optional): Vektordatenbank für fortgeschrittene KI-Workflows und Ähnlichkeitssuche
Infrastruktur und Monitoring:
- Cloudflare Tunnel: Sichere externe Konnektivität
- Traefik: Reverse Proxy mit automatischem SSL und Monitoring-Dashboard
Wie alles zusammenwirkt
Hier ist der vollständige Ablauf, wenn ein Kunde auf seine Umgebung zugreift:
- Der Kunde ruft seine eigene Domain auf (z. B.
workflows.client-a.com) - Cloudflare Tunnel leitet die Anfrage an Ihre lokale Traefik-Instanz weiter
- Traefik liest die Domain, wendet Middleware an (Authentifizierung, SSL, Rate Limiting) und leitet an den korrekten Kundencontainer weiter
- Authentik übernimmt die SSO-Authentifizierung über alle Dienste hinweg, falls konfiguriert
- Der Kunde erhält seine eigene isolierte Umgebung mit seinen Daten und Konfigurationen
- Die Umgebungen anderer Kunden sind über die Routen dieses Kunden nicht erreichbar
Alles bleibt geordnet und getrennt. Jeder Kunde erhält seine eigene Subdomain-Struktur, etwa auth.client-a.com, database.client-a.com, backend.client-a.com usw.
Die Einrichtung: Die praktischen Schritte
Das Fundament vorbereiten
Zunächst benötigen Sie eine Docker-Desktop-Installation und eine eingerichtete Domainverwaltung. Wir empfehlen eine Wildcard-DNS-Struktur, um neue Kunden einfach einzubinden:
# Install Docker Desktop (macOS)
brew install --cask docker
# Verify installation
docker --version
docker-compose --version
# Ensure sufficient resources for multi-container environments
# Recommended: 16GB RAM, 8+ CPU cores, 500GB+ SSD storage
Das Vorlagensystem erstellen
Der Schlüssel ist der vorlagenbasierte Ansatz. Statt jede Kundenumgebung manuell einzurichten, erstellen wir Vorlagen, die sofort mit kundenspezifischen Konfigurationen bereitgestellt werden können.
Erstellen Sie die umfassende Verzeichnisstruktur:
mkdir -p development-stack/{template,deployments}
cd development-stack/template
# Create service-specific configuration directories
mkdir -p {traefik,authentik,supabase,init}
Vollständige Vorlage für mehrere Dienste
Erstellen Sie eine umfassende docker-compose.yml-Vorlage mit allen 16 Diensten. Dies ist eine Entwicklungsvorlage: Das Traefik-Dashboard läuft ohne Authentifizierung, deshalb veröffentlicht die Vorlage es und PostgreSQL nur auf 127.0.0.1, wo andere Rechner sie nicht erreichen (siehe den Abschnitt zur Sicherheitsabsicherung für den Produktivbetrieb weiter unten):
version: '3.8'
networks:
default:
name: ${TENANT_NETWORK}
driver: bridge
services:
# External Connectivity
cloudflare-tunnel:
image: cloudflare/cloudflared:latest
container_name: ${TENANT_PREFIX}-tunnel
command: tunnel --no-autoupdate run --token ${CLOUDFLARE_TOKEN}
networks:
- default
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "cloudflared tunnel info ${TUNNEL_ID} || exit 1"]
interval: 30s
timeout: 10s
retries: 3
# Reverse Proxy & Load Balancer
traefik:
image: traefik:v3.0
container_name: ${TENANT_PREFIX}-traefik
command:
- "--api.dashboard=true"
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.network=${TENANT_NETWORK}"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.email=${ADMIN_EMAIL}"
- "--certificatesresolvers.letsencrypt.acme.storage=/acme.json"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
ports:
- "${TRAEFIK_PORT}:80"
- "${TRAEFIK_SECURE_PORT}:443"
- "127.0.0.1:${TRAEFIK_DASHBOARD_PORT}:8080"
networks:
- default
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik/acme.json:/acme.json
healthcheck:
test: ["CMD", "traefik", "healthcheck"]
interval: 30s
timeout: 10s
retries: 3
# Database Backend
postgres:
image: postgres:15-alpine
container_name: ${TENANT_PREFIX}-postgres
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_MULTIPLE_DATABASES: n8n,supabase,authentik,nocodb
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init:/docker-entrypoint-initdb.d
networks:
- default
ports:
- "127.0.0.1:${POSTGRES_PORT}:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 30s
timeout: 10s
retries: 5
deploy:
resources:
limits:
memory: 2G
reservations:
memory: 1G
# Workflow Automation
n8n:
image: n8nio/n8n:latest
container_name: ${TENANT_PREFIX}-n8n
environment:
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_DATABASE: n8n
DB_POSTGRESDB_USER: ${POSTGRES_USER}
DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
N8N_PROTOCOL: ${N8N_PROTOCOL}
N8N_HOST: ${N8N_DOMAIN}
N8N_PORT: 5678
N8N_SECURE_COOKIE: ${N8N_SECURE_COOKIE}
WEBHOOK_URL: https://${N8N_DOMAIN}
N8N_EDITOR_BASE_URL: https://${N8N_DOMAIN}
EXECUTIONS_DATA_PRUNE: "true"
EXECUTIONS_DATA_MAX_AGE: 168
volumes:
- n8n_data:/home/node/.n8n
networks:
- default
labels:
- "traefik.enable=true"
- "traefik.http.routers.n8n.rule=Host(`${N8N_DOMAIN}`)"
- "traefik.http.routers.n8n.tls.certresolver=letsencrypt"
- "traefik.http.services.n8n.loadbalancer.server.port=5678"
- "traefik.http.routers.n8n.middlewares=${AUTH_MIDDLEWARE}"
depends_on:
postgres:
condition: service_healthy
# No-Code Database Interface
nocodb:
image: nocodb/nocodb:latest
container_name: ${TENANT_PREFIX}-nocodb
environment:
NC_DB: "pg://postgres:${POSTGRES_PASSWORD}@postgres:5432/nocodb"
NC_PUBLIC_URL: https://${NOCODB_DOMAIN}
NC_DISABLE_TELE: "true"
NC_ADMIN_EMAIL: ${ADMIN_EMAIL}
NC_ADMIN_PASSWORD: ${NOCODB_ADMIN_PASSWORD}
volumes:
- nocodb_data:/usr/app/data
networks:
- default
labels:
- "traefik.enable=true"
- "traefik.http.routers.nocodb.rule=Host(`${NOCODB_DOMAIN}`)"
- "traefik.http.routers.nocodb.tls.certresolver=letsencrypt"
- "traefik.http.services.nocodb.loadbalancer.server.port=8080"
- "traefik.http.routers.nocodb.middlewares=${AUTH_MIDDLEWARE}"
depends_on:
postgres:
condition: service_healthy
# Supabase Backend Stack (5 containers)
supabase-studio:
image: supabase/studio:latest
container_name: ${TENANT_PREFIX}-supabase-studio
environment:
STUDIO_PG_META_URL: http://supabase-meta:8080
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
DEFAULT_ORGANIZATION_NAME: ${CLIENT_NAME}
DEFAULT_PROJECT_NAME: ${CLIENT_NAME} Project
SUPABASE_PUBLIC_URL: https://${SUPABASE_DOMAIN}
networks:
- default
labels:
- "traefik.enable=true"
- "traefik.http.routers.supabase-studio.rule=Host(`${SUPABASE_DOMAIN}`)"
- "traefik.http.routers.supabase-studio.tls.certresolver=letsencrypt"
- "traefik.http.services.supabase-studio.loadbalancer.server.port=3000"
- "traefik.http.routers.supabase-studio.middlewares=${AUTH_MIDDLEWARE}"
healthcheck:
disable: true
depends_on:
postgres:
condition: service_healthy
supabase-meta:
image: supabase/postgres-meta:latest
container_name: ${TENANT_PREFIX}-supabase-meta
environment:
PG_META_PORT: 8080
PG_META_DB_HOST: postgres
PG_META_DB_PORT: 5432
PG_META_DB_NAME: supabase
PG_META_DB_USER: ${POSTGRES_USER}
PG_META_DB_PASSWORD: ${POSTGRES_PASSWORD}
networks:
- default
depends_on:
postgres:
condition: service_healthy
supabase-auth:
image: supabase/gotrue:latest
container_name: ${TENANT_PREFIX}-supabase-auth
environment:
GOTRUE_API_HOST: 0.0.0.0
GOTRUE_API_PORT: 9999
GOTRUE_DB_DRIVER: postgres
GOTRUE_DB_DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/supabase
GOTRUE_SITE_URL: https://${SUPABASE_DOMAIN}
GOTRUE_JWT_SECRET: ${SUPABASE_JWT_SECRET}
GOTRUE_JWT_EXP: 3600
GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated
networks:
- default
depends_on:
postgres:
condition: service_healthy
supabase-rest:
image: postgrest/postgrest:latest
container_name: ${TENANT_PREFIX}-supabase-rest
environment:
PGRST_DB_URI: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/supabase
PGRST_DB_SCHEMAS: public,graphql_public
PGRST_DB_ANON_ROLE: anon
PGRST_JWT_SECRET: ${SUPABASE_JWT_SECRET}
PGRST_DB_USE_LEGACY_GUCS: "false"
networks:
- default
depends_on:
postgres:
condition: service_healthy
supabase-realtime:
image: supabase/realtime:latest
container_name: ${TENANT_PREFIX}-supabase-realtime
environment:
PORT: 4000
DB_HOST: postgres
DB_PORT: 5432
DB_USER: ${POSTGRES_USER}
DB_PASSWORD: ${POSTGRES_PASSWORD}
DB_NAME: supabase
DB_AFTER_CONNECT_QUERY: 'SET search_path TO _realtime'
DB_ENC_KEY: supabaserealtime
API_JWT_SECRET: ${SUPABASE_JWT_SECRET}
FLY_ALLOC_ID: fly123
FLY_APP_NAME: realtime
SECRET_KEY_BASE: ${SUPABASE_JWT_SECRET}
ERL_AFLAGS: -proto_dist inet_tcp
ENABLE_TAILSCALE: "false"
DNS_NODES: "''"
networks:
- default
command: >
sh -c "/app/bin/migrate && /app/bin/realtime eval 'Realtime.Release.seeds(Realtime.Repo)' && /app/bin/server"
depends_on:
postgres:
condition: service_healthy
supabase-kong:
image: kong:3.2-alpine
container_name: ${TENANT_PREFIX}-supabase-kong
environment:
KONG_DATABASE: "off"
KONG_DECLARATIVE_CONFIG: /var/lib/kong/kong.yml
KONG_DNS_ORDER: LAST,A,CNAME
KONG_PLUGINS: request-size-limiting,cors,key-auth,rate-limiting
KONG_NGINX_PROXY_PROXY_BUFFER_SIZE: 160k
KONG_NGINX_PROXY_PROXY_BUFFERS: 64 160k
volumes:
- ./supabase/kong.yml:/var/lib/kong/kong.yml:ro
networks:
- default
labels:
- "traefik.enable=true"
- "traefik.http.routers.kong.rule=Host(`api.${CLIENT_DOMAIN}`)"
- "traefik.http.routers.kong.tls.certresolver=letsencrypt"
- "traefik.http.services.kong.loadbalancer.server.port=8000"
- "traefik.http.routers.kong.middlewares=${AUTH_MIDDLEWARE}"
# Local AI Language Models
ollama:
image: ollama/ollama:latest
container_name: ${TENANT_PREFIX}-ollama
environment:
OLLAMA_HOST: 0.0.0.0:11434
OLLAMA_ORIGINS: "*"
volumes:
- ollama_data:/root/.ollama
networks:
- default
labels:
- "traefik.enable=true"
- "traefik.http.routers.ollama.rule=Host(`ai.${CLIENT_DOMAIN}`)"
- "traefik.http.routers.ollama.tls.certresolver=letsencrypt"
- "traefik.http.services.ollama.loadbalancer.server.port=11434"
- "traefik.http.routers.ollama.middlewares=${AUTH_MIDDLEWARE}"
deploy:
resources:
limits:
memory: 16G
reservations:
memory: 8G
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:11434/api/tags"]
interval: 30s
timeout: 10s
retries: 3
# Enterprise SSO Authentication (3 containers)
authentik-redis:
image: redis:alpine
container_name: ${TENANT_PREFIX}-authentik-redis
command: --save 60 1 --loglevel warning
networks:
- default
volumes:
- authentik_redis_data:/data
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
interval: 30s
timeout: 3s
retries: 3
authentik-server:
image: ghcr.io/goauthentik/server:${AUTHENTIK_TAG}
container_name: ${TENANT_PREFIX}-authentik-server
command: server
environment:
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
AUTHENTIK_ERROR_REPORTING__ENABLED: "false"
AUTHENTIK_POSTGRESQL__HOST: postgres
AUTHENTIK_POSTGRESQL__USER: ${POSTGRES_USER}
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: ${POSTGRES_PASSWORD}
AUTHENTIK_REDIS__HOST: authentik-redis
volumes:
- authentik_media:/media
- authentik_templates:/templates
networks:
- default
labels:
- "traefik.enable=true"
- "traefik.http.routers.authentik.rule=Host(`auth.${CLIENT_DOMAIN}`)"
- "traefik.http.routers.authentik.tls.certresolver=letsencrypt"
- "traefik.http.services.authentik.loadbalancer.server.port=9000"
depends_on:
postgres:
condition: service_healthy
authentik-redis:
condition: service_healthy
authentik-worker:
image: ghcr.io/goauthentik/server:${AUTHENTIK_TAG}
container_name: ${TENANT_PREFIX}-authentik-worker
command: worker
environment:
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
AUTHENTIK_ERROR_REPORTING__ENABLED: "false"
AUTHENTIK_POSTGRESQL__HOST: postgres
AUTHENTIK_POSTGRESQL__USER: ${POSTGRES_USER}
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: ${POSTGRES_PASSWORD}
AUTHENTIK_REDIS__HOST: authentik-redis
volumes:
- authentik_media:/media
- authentik_templates:/templates
- /var/run/docker.sock:/var/run/docker.sock
networks:
- default
depends_on:
postgres:
condition: service_healthy
authentik-redis:
condition: service_healthy
# Test Service for Health Monitoring
whoami:
image: traefik/whoami:latest
container_name: ${TENANT_PREFIX}-whoami
networks:
- default
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`test.${CLIENT_DOMAIN}`)"
- "traefik.http.routers.whoami.tls.certresolver=letsencrypt"
- "traefik.http.services.whoami.loadbalancer.server.port=80"
volumes:
postgres_data:
n8n_data:
nocodb_data:
ollama_data:
authentik_redis_data:
authentik_media:
authentik_templates:
Vollständige Umgebungsvorlage
Erstellen Sie .env.template mit allen kundenspezifischen Variablen:
# Client Configuration
CLIENT_NAME=CLIENT_NAME_PLACEHOLDER
CLIENT_DOMAIN=CLIENT_DOMAIN_PLACEHOLDER
TENANT_PREFIX=CLIENT_PREFIX_PLACEHOLDER
TENANT_NETWORK=CLIENT_NETWORK_PLACEHOLDER
# Service Domains (Subdomain-based routing)
N8N_DOMAIN=workflows.CLIENT_DOMAIN_PLACEHOLDER
NOCODB_DOMAIN=database.CLIENT_DOMAIN_PLACEHOLDER
SUPABASE_DOMAIN=backend.CLIENT_DOMAIN_PLACEHOLDER
AUTHENTIK_DOMAIN=auth.CLIENT_DOMAIN_PLACEHOLDER
# Infrastructure Ports
TRAEFIK_PORT=80
TRAEFIK_SECURE_PORT=443
TRAEFIK_DASHBOARD_PORT=8080
POSTGRES_PORT=5432
# Admin Configuration
ADMIN_EMAIL=admin@CLIENT_DOMAIN_PLACEHOLDER
# Database Configuration
POSTGRES_DB=main_db
POSTGRES_USER=postgres
POSTGRES_PASSWORD=SECURE_PASSWORD_PLACEHOLDER
# Service-Specific Passwords
NOCODB_ADMIN_PASSWORD=NOCODB_PASSWORD_PLACEHOLDER
SUPABASE_JWT_SECRET=SUPABASE_JWT_PLACEHOLDER
# Authentik SSO Configuration
AUTHENTIK_SECRET_KEY=AUTHENTIK_SECRET_PLACEHOLDER
AUTHENTIK_TAG=2024.8.3
# n8n Configuration
N8N_PROTOCOL=https
N8N_SECURE_COOKIE=true
# Cloudflare Integration
CLOUDFLARE_TOKEN=CLOUDFLARE_TOKEN_PLACEHOLDER
TUNNEL_ID=TUNNEL_ID_PLACEHOLDER
# Authentication Middleware (set to 'auth-global' for SSO, leave empty for no auth)
AUTH_MIDDLEWARE=
Datenbank-Initialisierungsskripte
Richten Sie die vollständige Datenbankinitialisierung in init/01-create-multiple-databases.sql ein:
-- Create databases for all services
CREATE DATABASE n8n;
CREATE DATABASE nocodb;
CREATE DATABASE supabase;
CREATE DATABASE authentik;
-- Grant permissions
GRANT ALL PRIVILEGES ON DATABASE n8n TO postgres;
GRANT ALL PRIVILEGES ON DATABASE nocodb TO postgres;
GRANT ALL PRIVILEGES ON DATABASE supabase TO postgres;
GRANT ALL PRIVILEGES ON DATABASE authentik TO postgres;
-- Enable required extensions for Supabase
\c supabase;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
-- Enable required extensions for Authentik
\c authentik;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
\echo 'Multiple databases and extensions created successfully'
Supabase-Kong-Gateway-Konfiguration
Erstellen Sie supabase/kong.yml für das API-Gateway-Routing:
_format_version: "3.0"
services:
- name: auth-v1-open
url: http://supabase-auth:9999/verify
plugins:
- name: cors
routes:
- name: auth-v1-open
strip_path: true
paths:
- /auth/v1/verify
methods:
- POST
- OPTIONS
- name: auth-v1-open-callback
url: http://supabase-auth:9999/callback
plugins:
- name: cors
routes:
- name: auth-v1-open-callback
strip_path: true
paths:
- /auth/v1/callback
methods:
- GET
- POST
- OPTIONS
- name: auth-v1
_comment: "GoTrue: /auth/v1/* -> http://supabase-auth:9999/*"
url: http://supabase-auth:9999/
plugins:
- name: cors
- name: key-auth
config:
hide_credentials: false
routes:
- name: auth-v1-all
strip_path: true
paths:
- /auth/v1/
methods:
- GET
- POST
- PUT
- PATCH
- DELETE
- OPTIONS
- name: rest-v1
_comment: "PostgREST: /rest/v1/* -> http://supabase-rest:3000/*"
url: http://supabase-rest:3000/
plugins:
- name: cors
- name: key-auth
config:
hide_credentials: true
routes:
- name: rest-v1-all
strip_path: true
paths:
- /rest/v1/
methods:
- GET
- POST
- PUT
- PATCH
- DELETE
- OPTIONS
- name: realtime-v1
_comment: "Realtime: /realtime/v1/* -> ws://supabase-realtime:4000/socket/*"
url: http://supabase-realtime:4000/socket/
plugins:
- name: cors
- name: key-auth
config:
hide_credentials: false
routes:
- name: realtime-v1-all
strip_path: true
paths:
- /realtime/v1/
methods:
- GET
- POST
- PUT
- PATCH
- DELETE
- OPTIONS
consumers:
- username: anon
keyauth_credentials:
- key: your-anon-key-here
- username: service_role
keyauth_credentials:
- key: your-service-role-key-here
plugins:
- name: cors
config:
origins:
- "*"
methods:
- GET
- POST
- PUT
- PATCH
- DELETE
- OPTIONS
headers:
- Accept
- Accept-Version
- Content-Length
- Content-MD5
- Content-Type
- Date
- X-Auth-Token
- Authorization
- X-Forwarded-For
- X-Forwarded-Proto
- X-Forwarded-Port
exposed_headers:
- X-Auth-Token
credentials: true
max_age: 3600
Automatisiertes Bereitstellungsskript
Mit diesem Bereitstellungsskript erstellen Sie neue Kundenumgebungen in Minuten. Es leitet alle Host-Ports (Traefik HTTP, HTTPS und Dashboard sowie PostgreSQL) aus einer Prüfsumme des Kundenpräfixes ab, sodass mehrere Kunden denselben Host nutzen können. Prüfen Sie, dass die Offsets zweier Kunden nicht kollidieren:
#!/bin/bash
# deploy-client.sh - Complete Multi-Tenant Deployment
CLIENT_DOMAIN=$1
CLIENT_NAME=$2
CLOUDFLARE_TOKEN=$3
if [ -z "$CLIENT_DOMAIN" ] || [ -z "$CLIENT_NAME" ] || [ -z "$CLOUDFLARE_TOKEN" ]; then
echo "Usage: ./deploy-client.sh example.com 'Client Name' 'cloudflare-token'"
echo ""
echo "Example: ./deploy-client.sh client-a.com 'Client A Corporation' 'your-cloudflare-token'"
exit 1
fi
CLIENT_PREFIX=$(echo $CLIENT_DOMAIN | sed 's/[.-]//g' | tr '[:upper:]' '[:lower:]')
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
echo "Deploying complete multi-tenant environment..."
echo "Configuration:"
echo " Domain: $CLIENT_DOMAIN"
echo " Name: $CLIENT_NAME"
echo " Prefix: $CLIENT_PREFIX"
echo " Timestamp: $TIMESTAMP"
echo ""
# Create deployment directory
DEPLOY_DIR="../deployments/$CLIENT_DOMAIN"
mkdir -p "$DEPLOY_DIR"/{traefik,authentik,supabase,init,logs}
echo "Created deployment directory structure"
# Copy template files
cp docker-compose.yml "$DEPLOY_DIR/"
cp -r {traefik,authentik,supabase,init}/ "$DEPLOY_DIR/" 2>/dev/null || true
echo "Copied configuration templates"
# Generate secure passwords and secrets
POSTGRES_PASSWORD=$(openssl rand -base64 32 | tr -d "=+/" | cut -c1-25)
NOCODB_PASSWORD=$(openssl rand -base64 16 | tr -d "=+/" | cut -c1-16)
SUPABASE_JWT_SECRET=$(openssl rand -base64 64 | tr -d "=+/" | cut -c1-64)
AUTHENTIK_SECRET=$(openssl rand -hex 32)
echo "Generated secure credentials"
# Calculate unique ports to avoid conflicts
PORT_OFFSET=$(($(echo "$CLIENT_PREFIX" | cksum | cut -f1 -d' ') % 1000))
TRAEFIK_DASHBOARD_PORT=$((8080 + PORT_OFFSET))
POSTGRES_PORT=$((5432 + PORT_OFFSET))
TRAEFIK_PORT=$((10000 + PORT_OFFSET))
TRAEFIK_SECURE_PORT=$((11000 + PORT_OFFSET))
# Create comprehensive environment file
cat .env.template | \
sed "s/CLIENT_NAME_PLACEHOLDER/$CLIENT_NAME/g" | \
sed "s/CLIENT_DOMAIN_PLACEHOLDER/$CLIENT_DOMAIN/g" | \
sed "s/CLIENT_PREFIX_PLACEHOLDER/$CLIENT_PREFIX/g" | \
sed "s/CLIENT_NETWORK_PLACEHOLDER/${CLIENT_PREFIX}-network/g" | \
sed "s/SECURE_PASSWORD_PLACEHOLDER/$POSTGRES_PASSWORD/g" | \
sed "s/NOCODB_PASSWORD_PLACEHOLDER/$NOCODB_PASSWORD/g" | \
sed "s/SUPABASE_JWT_PLACEHOLDER/$SUPABASE_JWT_SECRET/g" | \
sed "s/AUTHENTIK_SECRET_PLACEHOLDER/$AUTHENTIK_SECRET/g" | \
sed "s/CLOUDFLARE_TOKEN_PLACEHOLDER/$CLOUDFLARE_TOKEN/g" | \
sed "s/^TRAEFIK_PORT=80$/TRAEFIK_PORT=$TRAEFIK_PORT/" | \
sed "s/^TRAEFIK_SECURE_PORT=443$/TRAEFIK_SECURE_PORT=$TRAEFIK_SECURE_PORT/" | \
sed "s/8080/$TRAEFIK_DASHBOARD_PORT/g" | \
sed "s/5432/$POSTGRES_PORT/g" \
> "$DEPLOY_DIR/.env"
echo "Generated environment configuration"
# Create ACE file for Traefik SSL
touch "$DEPLOY_DIR/traefik/acme.json"
chmod 600 "$DEPLOY_DIR/traefik/acme.json"
# Initialize deployment
cd "$DEPLOY_DIR"
echo "Starting Docker containers..."
echo " This may take several minutes for first-time image downloads"
# Start core infrastructure first
docker-compose up -d cloudflare-tunnel traefik postgres
echo "Waiting for database to be ready..."
sleep 30
# Start all remaining services
docker-compose up -d
echo ""
echo "Multi-tenant environment deployed successfully!"
echo ""
echo "Access URLs:"
echo " Traefik Dashboard: http://localhost:$TRAEFIK_DASHBOARD_PORT"
echo " Workflows (n8n): https://workflows.$CLIENT_DOMAIN"
echo " Database (NocoDB): https://database.$CLIENT_DOMAIN"
echo " Backend (Supabase): https://backend.$CLIENT_DOMAIN"
echo " AI (Ollama): https://ai.$CLIENT_DOMAIN"
echo " Authentication: https://auth.$CLIENT_DOMAIN"
echo " API Gateway: https://api.$CLIENT_DOMAIN"
echo " Test Service: https://test.$CLIENT_DOMAIN"
echo ""
echo "Container Status:"
docker-compose ps
echo ""
echo "Generated Credentials (save these securely):"
echo " Client: $CLIENT_NAME"
echo " PostgreSQL Password: $POSTGRES_PASSWORD"
echo " NocoDB Admin Password: $NOCODB_PASSWORD"
echo " Supabase JWT Secret: [hidden - check .env file]"
echo ""
echo "Next Steps:"
echo " 1. Configure Cloudflare DNS: *.${CLIENT_DOMAIN} -> tunnel"
echo " 2. Wait 2-3 minutes for all services to initialize"
echo " 3. Access services via the URLs above"
echo " 4. Configure SSO via auth.$CLIENT_DOMAIN if needed"
echo ""
echo "Documentation: Visit tva.sg for setup guides and troubleshooting"
echo "Support: Contact us via tva.sg/contact for assistance"
Ihren mandantenfähigen Stack verwenden
Neue Kundenumgebungen bereitstellen
Mit unserem umfassenden Skript lässt sich eine neue Kundenumgebung einfach erstellen:
# Deploy Client A with full enterprise stack
./deploy-client.sh client-a.com "Client A Corporation" "your-cloudflare-token"
# Deploy Client B with different domain
./deploy-client.sh client-b.org "Client B Industries" "your-cloudflare-token"
# Deploy Startup C
./deploy-client.sh startup-c.io "Startup C" "your-cloudflare-token"
Jede Bereitstellung erstellt:
- Dediziertes Docker-Netzwerk mit 16 Containern
- Separate Daten-Volumes für persistente Speicherung
- Eigene Dienstcontainer mit Zustandsüberwachung
- Individuelle Cloudflare-Tunnel-Konfiguration
- Benutzerdefiniertes Domain-Routing mit SSL-Zertifikaten
- SSO-Infrastruktur auf Unternehmensniveau, bereit zur Aktivierung
Mehrere Umgebungen verwalten
Überwachen Sie alle Kundenumgebungen von einem zentralen Ort aus:
# Check all running environments across clients
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep -E "(client|startup)"
# View comprehensive logs for specific client
cd deployments/client-a.com
docker-compose logs -f --tail=50 n8n
# Health check all services for a client
docker-compose ps
docker-compose exec postgres pg_isready
# Restart specific services
docker-compose restart nocodb supabase-studio
# Update all services to latest images
docker-compose pull && docker-compose up -d
Container-Architektur im Detail
Unsere vollständige 16-Container-Architektur pro Kunde umfasst:
Infrastrukturschicht (4 Container):
cloudflare-tunnel: Sichere externe Konnektivitättraefik: Reverse Proxy mit automatischem SSL und Diensterkennungpostgres: Zentrale Datenbank mit Connection Poolingwhoami: Zustandsüberwachung und Routing-Prüfung
Anwendungsschicht (7 Container):
n8n: Workflow-Automatisierung mit PostgreSQL-Backendnocodb: No-Code-Datenbankoberflächesupabase-studio: Backend-Entwicklungs-Dashboardsupabase-meta: Datenbank-Introspektionsdienstsupabase-auth: Authentifizierung und Benutzerverwaltungsupabase-rest: Automatisch generierte REST-APIsupabase-realtime: Echtzeit-Abonnements und Updates
KI- und Gateway-Schicht (2 Container):
ollama: Lokale KI mit GPU-Beschleunigungsunterstützungsupabase-kong: API-Gateway mit Rate Limiting und CORS
Sicherheitsschicht für Unternehmen (3 Container):
authentik-server: SSO-Authentifizierungsserverauthentik-worker: Hintergrundaufgaben und Benachrichtigungenauthentik-redis: Sitzungsverwaltung und Caching
Ressourcen pro Kunde skalieren
Passen Sie Ressourcen basierend auf Kundenbedürfnissen und Nutzungsmustern an:
# High-performance client configuration
services:
n8n:
deploy:
resources:
limits:
cpus: '4.0'
memory: 8G
reservations:
cpus: '2.0'
memory: 4G
postgres:
deploy:
resources:
limits:
cpus: '2.0'
memory: 4G
reservations:
cpus: '1.0'
memory: 2G
environment:
- POSTGRES_MAX_CONNECTIONS=200
- POSTGRES_SHARED_BUFFERS=1GB
- POSTGRES_EFFECTIVE_CACHE_SIZE=3GB
ollama:
deploy:
resources:
limits:
memory: 32G
reservations:
memory: 16G
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
Echte Vorteile für Ihr Unternehmen
Vollständige Mandantentrennung mit Funktionen auf Unternehmensniveau
Jeder Kunde erhält seine eigene vollständige Umgebung mit SSO auf Unternehmensniveau, KI-Funktionen und kompletter Backend-Infrastruktur. Daten, Konfigurationen, Anpassungen und Sicherheitsrichtlinien bleiben getrennt. Ein Problem auf Anwendungsebene in einer Kundenumgebung bleibt auf diese Umgebung beschränkt. Da jedoch alle Kunden denselben Host nutzen, können Probleme auf Host-Ebene wie erschöpfte CPU-, Arbeitsspeicher- oder Festplattenressourcen weiterhin alle betreffen. Dies ähnelt der Trennung, die wir mit unseren einzelnen n8n-Deployments erreichen.
Schnelles Kunden-Onboarding mit vollem Funktionsumfang
Neue Kunden können in unter 10 Minuten mit einem vollständigen Entwicklungs- und Automatisierungsstack starten. Das Deployment-Skript automatisiert die Konfiguration, die Erzeugung von Zugangsdaten und die Initialisierung der Dienste. Cloudflare-DNS-Einträge und optionales SSO bleiben kurze manuelle Schritte, wie die abschließenden Anweisungen des Skripts zeigen.
Planbare Kosten für Funktionen auf Unternehmensniveau
Nach der Ersteinrichtung fallen neben der Basisinfrastruktur keine kundenspezifischen Hosting-Kosten an. Anders als bei SaaS-Lösungen, die pro Nutzer, Workflow oder API-Aufruf abrechnen, bezahlen Sie einen festen Betrag für den Server und können so viele Kundenumgebungen mit vollem Funktionsumfang auf Unternehmensniveau betreiben, wie seine Ressourcen zulassen.
Professionelle Markenkonsistenz
Jeder Kunde erhält Domains unter seiner eigenen Marke mit professionellen Subdomains (workflows.client.com, auth.client.com usw.) und kann seine Umgebungen vollständig anpassen. Es gibt keine „powered by“-Fußzeilen oder gemeinsam genutzten Oberflächen, die seine Markenidentität verwässern.
Die n8n-Integration: Workflow-Automatisierung für Unternehmen in großem Umfang
Hier entfaltet der Ansatz seine volle Leistungsfähigkeit. Wie in unserem Leitfaden, in dem wir zeigen, wie Sie n8n für die Workflow-Automatisierung selbst betreiben können, erhält jeder Kunde in diesem mandantenfähigen Setup seine eigene vollständige n8n-Instanz, integriert in einen kompletten Stack für den Unternehmenseinsatz.
Jeder Kunde kann anspruchsvolle Workflows erstellen, die:
- sich mit den eigenen Datenbanken verbinden (NocoDB, Supabase PostgreSQL)
- eigene KI-Modelle (Ollama) für intelligente Automatisierung nutzen
- die Authentifizierung über SSO für Unternehmen (Authentik) durchführen
- die jeweiligen Geschäftsanwendungen und APIs integrieren
- die Daten in ihrer eigenen isolierten Umgebung verarbeiten
Diese Kombination schafft eine leistungsfähige Plattform für die Bereitstellung von Kundenlösungen, mit der Sie:
- standardisierte Automatisierungsfunktionen schnell bereitstellen
- Workflows je Kunde anpassen, ohne andere zu beeinflussen
- Ihre Leistungserbringung ohne lineare Kostensteigerungen skalieren
- die vollständige Datensouveränität jedes Kunden wahren
- SSO-basierte Zugriffskontrolle als Grundlage für Sicherheit und Compliance bieten
Dieser Ansatz baut auf denselben Prinzipien wie unser Leitfaden zur Einrichtung von Windmill mit Docker auf, erweitert sie aber zu einer vollständigen mandantenfähigen Architektur.
Erweiterte Konfigurationsoptionen
SSO auf Unternehmensniveau mit Authentik implementieren
Aktivieren Sie Single Sign-on über alle Kundendienste hinweg, indem Sie die Forward-Authentifizierung von Authentik konfigurieren:
# Add to Traefik middleware configuration
middlewares:
auth-global:
forwardAuth:
address: "http://authentik-server:9000/outpost.goauthentik.io/auth/traefik"
trustForwardHeader: true
authResponseHeaders:
- X-authentik-username
- X-authentik-groups
- X-authentik-email
- X-authentik-name
- X-authentik-uid
Aktualisieren Sie anschließend die Labels Ihrer Dienste, damit sie die Middleware verwenden:
labels:
- "traefik.http.routers.n8n.middlewares=auth-global"
- "traefik.http.routers.nocodb.middlewares=auth-global"
- "traefik.http.routers.supabase-studio.middlewares=auth-global"
Eine Vektordatenbank für erweiterte KI-Funktionen hinzufügen
Erweitern Sie die KI-Funktionen mit der Qdrant-Vektordatenbank:
qdrant:
image: qdrant/qdrant:latest
container_name: ${TENANT_PREFIX}-qdrant
environment:
QDRANT__SERVICE__HTTP_PORT: 6333
QDRANT__SERVICE__GRPC_PORT: 6334
volumes:
- qdrant_data:/qdrant/storage
networks:
- default
labels:
- "traefik.enable=true"
- "traefik.http.routers.qdrant.rule=Host(`vector.${CLIENT_DOMAIN}`)"
- "traefik.http.routers.qdrant.tls.certresolver=letsencrypt"
- "traefik.http.services.qdrant.loadbalancer.server.port=6333"
- "traefik.http.routers.qdrant.middlewares=${AUTH_MIDDLEWARE}"
Hybride KI-Architektur implementieren
Für optimale Leistung empfiehlt sich ein hybrider Ansatz, der containerisierte und native KI kombiniert:
# Install Ollama natively on host for GPU acceleration
brew install ollama
# Configure containers to use native Ollama
# In docker-compose.yml, services can access via host.docker.internal:11434
n8n:
environment:
- OLLAMA_HOST=host.docker.internal:11434
Unter macOS ist dies relevant, weil Docker Desktop keine GPUs in Containern unterstützt, während das nativ ausgeführte Ollama die GPU-Beschleunigung direkt nutzt. In unserem Aufbau ergab dies eine ungefähr 5-6-fache Leistungsverbesserung, während die anderen Dienste weiterhin in Containern liefen.
Monitoring- und Observability-Stack
Fügen Sie umfassendes Monitoring pro Kunde hinzu:
prometheus:
image: prom/prometheus:latest
container_name: ${TENANT_PREFIX}-prometheus
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
networks:
- default
labels:
- "traefik.enable=true"
- "traefik.http.routers.prometheus.rule=Host(`metrics.${CLIENT_DOMAIN}`)"
grafana:
image: grafana/grafana:latest
container_name: ${TENANT_PREFIX}-grafana
environment:
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD}
GF_USERS_ALLOW_SIGN_UP: "false"
volumes:
- grafana_data:/var/lib/grafana
- ./monitoring/dashboards:/var/lib/grafana/dashboards
networks:
- default
labels:
- "traefik.enable=true"
- "traefik.http.routers.grafana.rule=Host(`monitoring.${CLIENT_DOMAIN}`)"
- "traefik.http.routers.grafana.middlewares=${AUTH_MIDDLEWARE}"
Häufige Probleme und Lösungen
„Service Unavailable“ oder HTTP-502-Fehler
Das bedeutet in der Regel, dass Traefik den Zielcontainer nicht erreichen kann. Prüfen Sie Folgendes:
# Verify container is running and healthy
docker-compose ps
docker-compose logs traefik --tail=20
# Check container is on correct network
docker network ls
docker network inspect ${CLIENT_PREFIX}-network
# Verify Traefik labels are correct
docker-compose config --services
DNS-Auflösungsprobleme
Die Wildcard-DNS-Einrichtung ist entscheidend für das Subdomain-Routing:
# Correct Cloudflare DNS configuration
*.client-a.com CNAME tunnel-uuid.cfargotunnel.com
*.client-b.org CNAME tunnel-uuid2.cfargotunnel.com
# Test DNS resolution
nslookup workflows.client-a.com
dig workflows.client-a.com
Ressourcenengpässe bei mehreren Kunden
Überwachen Sie die Ressourcennutzung über alle Kundenumgebungen hinweg:
# Check overall system resource usage
docker stats --no-stream
htop
# Check disk usage per client
du -sh deployments/*/
df -h
# Monitor container memory usage
docker-compose -f deployments/*/docker-compose.yml ps --format "table {{.Name}}\t{{.Size}}"
Erschöpfung des Verbindungspools der Datenbank
PostgreSQL-Verbindungslimits können bei vielen Kunden erreicht werden. Konfigurieren Sie pro Bereitstellung:
-- Connect to client database
docker-compose exec postgres psql -U postgres
-- Increase connection limit
ALTER SYSTEM SET max_connections = 300;
ALTER SYSTEM SET shared_buffers = '256MB';
ALTER SYSTEM SET effective_cache_size = '1GB';
-- Reload configuration
SELECT pg_reload_conf();
Authentik-SSO-Konfigurationsprobleme
Häufige SSO-Setup-Probleme und Lösungen:
# Check Authentik containers are running
docker-compose ps | grep authentik
# Verify database initialization
docker-compose exec postgres psql -U postgres -d authentik -c "\dt"
# Check Authentik logs for startup issues
docker-compose logs authentik-server --tail=50
# Reset Authentik admin password if needed
docker-compose exec authentik-server ak create_admin_group
docker-compose exec authentik-server ak bootstrap_tasks
Probleme mit der Cloudflare-Tunnel-Verbindung
Analysieren Sie Probleme mit der Tunnelverbindung:
# Check tunnel status
docker-compose logs cloudflare-tunnel --tail=20
# Verify tunnel configuration in Cloudflare dashboard
# Ensure wildcard routing: *.client-domain.com
# Test tunnel connectivity
curl -I https://test.client-domain.com
Überlegungen zur Infrastruktur
Dimensionierung Ihrer Infrastruktur für mehrere Kunden
Für ein typisches Setup mit 10-15 gleichzeitigen Kunden mit vollständigen 16-Container-Stacks:
Mindestanforderungen:
- CPU: 16-24 Kerne (2 Kerne pro aktiver Kundenumgebung)
- RAM: 64-128 GB (4-8 GB pro Kunde je nach KI-Nutzung)
- Speicher: NVMe SSD mit 2 TB+ (Datenbanken, KI-Modelle und Logs wachsen mit der Zeit)
- Netzwerk: Gigabit-Verbindung für reaktionsschnellen Kundenzugriff
Empfohlen für den Produktivbetrieb:
- Server: Hetzner CCX62 oder vergleichbar (48 vCPU, 192 GB RAM)
- Speicher: 4 TB NVMe mit automatisiertem Backup-System
- Netzwerk: Mehrere redundante Verbindungen
- Monitoring: Vollständiger Observability-Stack mit Alarmierung
Backup-Strategie für mehrere Kundenumgebungen
Implementieren Sie automatisierte Backups pro Kunde:
#!/bin/bash
# backup-all-clients.sh - Comprehensive backup solution
BACKUP_DIR="/opt/backups"
BACKUP_DATE=$(date +%Y%m%d_%H%M%S)
STACK_DIR=$(pwd) # run from the development-stack directory
mkdir -p "$BACKUP_DIR"
for client_dir in "$STACK_DIR"/deployments/*/; do
if [ -d "$client_dir" ]; then
CLIENT_DOMAIN=$(basename "$client_dir")
echo "Backing up client: $CLIENT_DOMAIN"
cd "$client_dir"
# Backup databases with compression
docker-compose exec -T postgres pg_dumpall -U postgres | gzip > "${BACKUP_DIR}/${CLIENT_DOMAIN}_db_${BACKUP_DATE}.sql.gz"
# Backup persistent volumes
docker run --rm \
-v "${BACKUP_DIR}":/backup \
-v "${CLIENT_DOMAIN//.}_n8n_data":/data/n8n:ro \
-v "${CLIENT_DOMAIN//.}_nocodb_data":/data/nocodb:ro \
-v "${CLIENT_DOMAIN//.}_ollama_data":/data/ollama:ro \
alpine tar czf "/backup/${CLIENT_DOMAIN}_volumes_${BACKUP_DATE}.tar.gz" -C /data .
# Backup configuration files
tar czf "${BACKUP_DIR}/${CLIENT_DOMAIN}_config_${BACKUP_DATE}.tar.gz" \
docker-compose.yml .env traefik/ supabase/ authentik/
echo "Backup completed for $CLIENT_DOMAIN"
fi
done
# Cleanup old backups (keep 30 days)
find "$BACKUP_DIR" -name "*.gz" -mtime +30 -delete
# Optional: Upload to cloud storage
# rclone sync "$BACKUP_DIR" s3:backup-bucket/multi-tenant/
Sicherheitshärtung für den Produktivbetrieb
Implementieren Sie umfassende Best Practices für die Sicherheit:
# Enhanced Traefik security configuration
traefik:
command:
- "--api.dashboard=true"
- "--api.debug=false"
- "--log.level=WARN"
- "--accesslog=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
- "--providers.docker.exposedbydefault=false"
labels:
# Security headers middleware
- "traefik.http.middlewares.security.headers.customRequestHeaders.X-Forwarded-Proto=https"
- "traefik.http.middlewares.security.headers.customResponseHeaders.X-Frame-Options=DENY"
- "traefik.http.middlewares.security.headers.customResponseHeaders.X-Content-Type-Options=nosniff"
- "traefik.http.middlewares.security.headers.customResponseHeaders.Strict-Transport-Security=max-age=31536000"
- "traefik.http.middlewares.security.headers.customResponseHeaders.Content-Security-Policy=default-src 'self'"
# Rate limiting middleware
- "traefik.http.middlewares.ratelimit.ratelimit.burst=100"
- "traefik.http.middlewares.ratelimit.ratelimit.average=50"
Wenden Sie die Sicherheits-Middleware auf alle Kundendienste an:
labels:
- "traefik.http.routers.n8n.middlewares=security,ratelimit,${AUTH_MIDDLEWARE}"
Kostenanalyse: Die Zahlen, die zählen
Herkömmliche SaaS-Kosten (10 Unternehmenskunden mit vollem Funktionsumfang)
Monatliche Kosten pro Kunde:
- n8n Pro: 50 $/Monat pro Kunde = 500 $/Monat
- Supabase Pro: 25 $/Monat pro Kunde = 250 $/Monat
- NoCode-Plattform (Airtable): 20 $/Monat pro Kunde = 200 $/Monat
- SSO für Unternehmen (Auth0): 23 $/Monat pro Kunde = 230 $/Monat
- KI-API-Kosten (OpenAI): 50 $/Monat pro Kunde = 500 $/Monat
- Gesamt: 1.680 $/Monat = 20.160 $/Jahr
Kosten eines selbst betriebenen, mandantenfähigen Stacks für Unternehmen
Jährliche Infrastrukturkosten:
- Cloud-Server mit dedizierten vCPUs (Hetzner CCX62): 350 $/Monat = 4.200 $/Jahr
- Domain-Kosten (10 Kunden): 120 $/Jahr
- Cloudflare Pro (optional): 240 $/Jahr
- Gesamt: 4.560 $/Jahr
Jährliche Einsparungen: 15.600 $ (77 % Kostenreduktion)
Diese Zahlen spiegeln die Preise bei Veröffentlichung dieses Leitfadens im August 2025 wider. Stand Oktober 2026 beginnt der B2C-Essentials-Tarif von Auth0 bei 35 $/Monat, und Hetzner führt den CCX62 nicht mehr auf. Sein Nachfolger CCX63 mit 48 vCPUs und 192 GB wird mit 853,99 €/Monat zuzüglich Umsatzsteuer, einschließlich einer primären IPv4-Adresse gelistet. Berechnen Sie vor Ihrer Entscheidung beide Seiten mit aktuellen Preisen neu.
Zusätzlich erhalten Sie:
- Vollständige Datensouveränität und Datenschutz
- Unbegrenzte Anpassung und White-Labeling
- Keine Anbieterbindung oder Aufruflimits von SaaS-Tarifen
- Sicherheits- und Compliance-Kontrollen, die Sie selbst verwalten
- Möglichkeit, Reseller-Dienste anzubieten
- Vollständige Kontrolle über Updates und Funktionen
Das ist besonders vorteilhaft, weil unser Setup Funktionen auf Unternehmensniveau bietet, die in SaaS-Abonnements normalerweise deutlich mehr kosten würden – ähnlich wie die Kostenvorteile in unserer Analyse zum Self-Hosting von n8n.
WordPress-Integration: Workflows für Inhalte optimieren
Für Agenturen und Teams, die neben ihren Entwicklungsumgebungen auch WordPress-Websites verwalten, lässt sich dieser mandantenfähige Stack hervorragend in WordPress-Automatisierungsworkflows integrieren. So wie unser Plugin tva Duplicate Pro die Inhaltsverwaltung in WordPress vereinfacht, kann diese containerisierte Umgebung komplexe Workflows zwischen WordPress-Websites und Ihrer Entwicklungsinfrastruktur automatisieren.
WordPress-Integrationsmöglichkeiten:
- Weiterverteilung von Inhalten: n8n-Workflows, die WordPress-Inhalte automatisch in Kundensysteme übertragen
- Automatisierte Bereitstellungen: WordPress-Seitenänderungen lösen Bereitstellungen in Kundenumgebungen aus
- Datensynchronisierung: Änderungen in Kundendatenbanken (über NocoDB) aktualisieren automatisch WordPress-Inhalte
- KI-gestützte Inhalte: Ollama-Modelle generieren Inhalte, die in WordPress-Seiten einfließen
- Berichte für Kunden: Automatisierte WordPress-Berichte aus Kennzahlen der Entwicklungsumgebungen
So entsteht ein umfassendes Ökosystem, in dem die Inhaltsverwaltung in WordPress, Entwicklungsworkflows und die Bereitstellung von Kundenlösungen reibungslos zusammenarbeiten.
Lohnt sich der Einrichtungsaufwand?
Wenn Sie Entwicklungsumgebungen für mehrere Kunden verwalten, ein SaaS-Geschäft aufbauen oder eine Agentur betreiben, die technische Lösungen liefert – auf jeden Fall. Die Ersteinrichtung dauert etwa einen Tag, aber Sie erhalten:
Sofortige Vorteile:
- Automatisiertes Kunden-Onboarding in unter 10 Minuten mit einem vollständigen Stack für Unternehmen
- Starke Isolation zwischen Kundenumgebungen mit professionellem Branding
- Massive Kosteneinsparungen im Vergleich zu verwalteten Diensten (75 %+ Reduktion)
- Vollständige Kontrolle über Daten, Anpassungen und Compliance
- Skalierbare Architektur, die mit Ihrem Unternehmen wächst
- SSO-basierte Zugriffskontrolle mit Authentik
Langfristiger Wert:
- Kundenbindung durch überlegene Leistungserbringung und professionelle Präsentation
- Umsatzwachstum durch die Möglichkeit, mehr Kunden effizient zu bedienen
- Wettbewerbsvorteil durch Funktionen auf Unternehmensniveau zu wettbewerbsfähigen Preisen
- Technische Expertise, die Sie am Markt hervorhebt
Für Agenturen mit mehreren Kunden oder SaaS-Startups, die beim Skalieren die Kontrolle behalten möchten, bietet dieses Setup Funktionen auf Unternehmensniveau ohne die entsprechenden hohen Kosten. Containerisierung, automatisierte Bereitstellung, korrektes Domain-Routing und Sicherheit auf Unternehmensniveau schaffen eine Grundlage für deutliches Geschäftswachstum, die vollständig unter Ihrer Kontrolle bleibt.
Das Setup wird noch wertvoller, wenn Sie die Integrationsmöglichkeiten mit bestehenden Tools wie WordPress-Automatisierung und die bewährte Stabilität selbst betriebener Automatisierungsplattformen berücksichtigen.
Wie geht es weiter?
Wir arbeiten aktiv an Erweiterungen dieser mandantenfähigen Architektur. Künftige Tutorials behandeln:
Erweiterte Bereitstellungsoptionen:
- Kubernetes-Migrationsleitfaden für maximale Skalierbarkeit und Bereitstellung im Unternehmen
- Automatisierte SSL-Zertifikatsverwaltung mit integrierten Let’s-Encrypt-Workflows
- Erweitertes Monitoring und Alarmierung mit Prometheus, Grafana und benutzerdefinierten Dashboards
- Automatisierte Notfallwiederherstellung mit Backup-Strategien über mehrere Regionen hinweg
Verbesserungen der Kundenerfahrung:
- Self-Service-Portal für Kunden zur Verwaltung ihrer eigenen Umgebungen und Einstellungen
- White-Label-Anpassung mit Vorlagen für das Branding Ihrer Agentur
- Erweiterte Workflow-Vorlagen für gängige Geschäftsprozesse
- Integrationsleitfäden für beliebte Geschäftsanwendungen und APIs
Funktionen für Unternehmen:
- Erweiterte Sicherheitshärtung mit WAF und Intrusion Detection
- Compliance-Frameworks für DSGVO, SOC2 und andere Vorschriften
- Bereitstellung über mehrere Regionen hinweg mit Strategien für einen globalen Kundenstamm
- Leitfäden zur Leistungsoptimierung für Umgebungen mit hohem Datenverkehr
Die Zukunft der Leistungserbringung für Kunden liegt nicht in der Wahl zwischen Kontrolle und Komfort, sondern im Aufbau von Systemen, die beides bieten, effizient skalieren und professionelle Standards wahren.
Professionelle Unterstützung
Die Einrichtung einer mandantenfähigen Umgebung mit 16 containerisierten Diensten erfordert das Zusammenspiel vieler Komponenten. Unsere Dokumentation ist umfassend, doch jedes Unternehmen hat eigene Anforderungen und muss seine bestehende Infrastruktur berücksichtigen.
Wenn Sie dieses Setup für den Produktivbetrieb umsetzen oder an Ihre spezifischen Anforderungen bei der Bereitstellung von Kundenlösungen anpassen möchten, unterstützt unser Team Sie bei:
- maßgeschneiderten Deployment-Strategien für Ihre Infrastruktur
- der Integration bestehender Systeme und Workflows
- der Leistungsoptimierung für die Last Ihrer Kundenumgebungen
- der Sicherheitshärtung zur Erfüllung von Compliance-Anforderungen
- Mitarbeiterschulungen zur Verwaltung mandantenfähiger Umgebungen
- Strategien für laufende Wartung und Monitoring
Kontaktieren Sie uns über tva.sg, um Ihre Anforderungen an eine mandantenfähige Architektur zu besprechen und professionelle Beratung zur Umsetzung zu erhalten.
Ob Sie eine bestehende Agentur skalieren, eine neue SaaS-Plattform starten oder die Bereitstellung von Kundenlösungen auf Unternehmensniveau aufbauen: Wir helfen Ihnen, mit selbst betriebenen, containerisierten Lösungen erfolgreich zu sein, die Ihre Unabhängigkeit wahren und professionelle Ergebnisse liefern.