tva
← Insights

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:

  1. Der Kunde ruft seine eigene Domain auf (z. B. workflows.client-a.com)
  2. Cloudflare Tunnel leitet die Anfrage an Ihre lokale Traefik-Instanz weiter
  3. Traefik liest die Domain, wendet Middleware an (Authentifizierung, SSL, Rate Limiting) und leitet an den korrekten Kundencontainer weiter
  4. Authentik übernimmt die SSO-Authentifizierung über alle Dienste hinweg, falls konfiguriert
  5. Der Kunde erhält seine eigene isolierte Umgebung mit seinen Daten und Konfigurationen
  6. 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ät
  • traefik: Reverse Proxy mit automatischem SSL und Diensterkennung
  • postgres: Zentrale Datenbank mit Connection Pooling
  • whoami: Zustandsüberwachung und Routing-Prüfung

Anwendungsschicht (7 Container):

  • n8n: Workflow-Automatisierung mit PostgreSQL-Backend
  • nocodb: No-Code-Datenbankoberfläche
  • supabase-studio: Backend-Entwicklungs-Dashboard
  • supabase-meta: Datenbank-Introspektionsdienst
  • supabase-auth: Authentifizierung und Benutzerverwaltung
  • supabase-rest: Automatisch generierte REST-API
  • supabase-realtime: Echtzeit-Abonnements und Updates

KI- und Gateway-Schicht (2 Container):

  • ollama: Lokale KI mit GPU-Beschleunigungsunterstützung
  • supabase-kong: API-Gateway mit Rate Limiting und CORS

Sicherheitsschicht für Unternehmen (3 Container):

  • authentik-server: SSO-Authentifizierungsserver
  • authentik-worker: Hintergrundaufgaben und Benachrichtigungen
  • authentik-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.

Weiterführende Beiträge

Weiterführende Beiträge