tva
← Artigos

Construção de uma stack de desenvolvimento multi-tenant com Docker: configuração completa para implantações escaláveis por cliente

Como criar um ambiente de desenvolvimento multi-tenant baseado em modelos reutilizáveis, com 16 serviços executados em contêineres, que funciona offline e permanece acessível online através de roteamento baseado em subdomínios.

Gerenciar ambientes de desenvolvimento para múltiplos clientes frequentemente significa escolher entre configurações manuais complexas ou soluções caras na nuvem. Implantações manuais são demoradas e propensas a erros. Plataformas na nuvem são convenientes, mas criam dependência de fornecedor e custos contínuos que aumentam com o uso.

Hoje, vamos percorrer a construção de uma stack de desenvolvimento multi-tenant escalável que oferece isolamento robusto entre ambientes de clientes e recursos de implantação automatizada, mantendo controle total sobre sua infraestrutura. Essa abordagem se baseia em nossa filosofia de soluções auto-hospedadas — assim como mostramos que é possível auto-hospedar o n8n para automação de fluxos de trabalho e implantar o Windmill com Docker para controle operacional completo.

As ferramentas que estamos usando

Vamos começar entendendo o que cada peça faz em nossa arquitetura abrangente de 16 contêineres:

Docker: a base para seus contêineres

O Docker fornece o isolamento e a consistência que precisamos para ambientes multi-tenant. Cada cliente recebe seus próprios contêineres com configurações idênticas, o que ajuda a garantir que o que funciona em desenvolvimento funcionará em produção. Pense nisso como ter múltiplos servidores separados em execução no mesmo hardware, embora ainda compartilhem sua CPU, memória, disco e kernel.

A vantagem principal? Isolamento robusto entre clientes. Os dados, configurações e personalizações de cada cliente ficam em contêineres, redes e volumes separados, para não interferirem nos de outro. Isso importa ao lidar com múltiplos clientes empresariais com diferentes requisitos e necessidades de segurança.

Traefik: proxy reverso e balanceador de carga inteligente

O Traefik age como um direcionador de tráfego inteligente, roteando automaticamente requisições para o ambiente de cliente correto com base em nomes de domínio. Em vez de configurar manualmente regras complexas de Apache ou Nginx, o Traefik lê rótulos dos seus contêineres Docker e configura o roteamento automaticamente.

Pense no Traefik como uma recepcionista que sabe exatamente para qual escritório (contêiner) cada visitante (requisição) deve ir, sem que você precise dar direções a cada vez. Em nossa configuração, o Traefik lida com terminação SSL, descoberta automática de serviços e fornece painéis de monitoramento detalhados.

Cloudflare Tunnels: acesso externo seguro

Cloudflare Tunnels fornecem acesso seguro à sua stack de desenvolvimento local sem configurações complexas de firewall ou VPNs. Cada domínio de cliente recebe seu próprio túnel, o que mantém separado o caminho de acesso externo de cada cliente.

A vantagem é que seus ambientes de desenvolvimento permanecem locais e seguros, mas os clientes podem acessar seus serviços específicos de qualquer lugar com autenticação adequada — de forma semelhante ao acesso externo seguro que configuramos em nosso guia de hospedagem do n8n.

A stack de serviços completa: tudo que seus clientes precisam

Nossa stack multi-tenant inclui quatro categorias de serviços principais em 16 contêineres por cliente:

Automação de fluxos de trabalho e lógica de negócios:

  • n8n: Plataforma completa de automação de fluxos de trabalho para automação de processos de negócio
  • Authentik: Autenticação única (SSO) e gerenciamento de identidade de nível empresarial (3 contêineres: servidor, worker, cache Redis)

Banco de dados e serviços de backend:

  • PostgreSQL: Backend de banco de dados robusto que atende a todos os serviços com gerenciamento otimizado do pool de conexões
  • Stack Supabase: Backend como serviço completo com 6 contêineres especializados (Studio, Meta, Auth, REST API, Realtime, Kong Gateway)
  • NocoDB: Interface de banco de dados sem código para gestão de dados do cliente

IA e inteligência:

  • Ollama: Modelos de linguagem de IA locais com aceleração por GPU para automação inteligente
  • Qdrant (opcional): Banco de dados vetorial para fluxos avançados de IA e busca por similaridade

Infraestrutura e monitoramento:

  • Cloudflare Tunnel: Conectividade externa segura
  • Traefik: Proxy reverso com SSL automático e painel de monitoramento

Como tudo funciona em conjunto

Este é o fluxo completo quando um cliente acessa seu ambiente:

  1. O cliente acessa seu domínio personalizado (por exemplo, workflows.client-a.com)
  2. O Cloudflare Tunnel encaminha a requisição para sua instância local do Traefik
  3. O Traefik lê o domínio, aplica middleware (autenticação, SSL e limite de requisições) e encaminha a requisição ao contêiner correto do cliente
  4. O Authentik gerencia a autenticação única (SSO) em todos os serviços, se estiver configurado
  5. O cliente acessa seu próprio ambiente isolado, com seus dados e configurações
  6. Os ambientes dos outros clientes não são acessíveis pelas rotas deste cliente

Tudo permanece organizado e separado, com cada cliente recebendo sua própria estrutura de subdomínios, como auth.client-a.com, database.client-a.com, backend.client-a.com etc.

Como configurar: etapas práticas

Preparação da base

Primeiro, você precisará ter o Docker Desktop instalado e uma estrutura de gerenciamento de domínios. Recomendamos configurar uma estrutura de DNS com curinga para facilitar a configuração inicial de novos clientes:

# 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

Criação do sistema de modelos

O segredo está na abordagem baseada em modelos reutilizáveis. Em vez de configurar cada cliente manualmente, criamos modelos que podem ser implantados imediatamente com configurações específicas de cada cliente.

Crie a estrutura completa de diretórios:

mkdir -p development-stack/{template,deployments}
cd development-stack/template

# Create service-specific configuration directories
mkdir -p {traefik,authentik,supabase,init}

Configuração completa do modelo com múltiplos serviços

Crie um modelo completo de docker-compose.yml com todos os 16 serviços. Este é um modelo de desenvolvimento: o painel do Traefik funciona sem autenticação, por isso o modelo o publica, junto com o PostgreSQL, apenas em 127.0.0.1, onde outras máquinas não conseguem acessá-los (veja a seção sobre reforço da segurança em produção abaixo):

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:

Modelo completo de variáveis de ambiente

Crie .env.template com todas as variáveis específicas de cada cliente:

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

Scripts de inicialização dos bancos de dados

Crie a configuração completa de inicialização dos bancos de dados em init/01-create-multiple-databases.sql:

-- 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'

Configuração do Kong Gateway do Supabase

Crie supabase/kong.yml para o roteamento do gateway de API:

_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

Script de implantação automatizada

Use este script de implantação para criar novos ambientes de clientes em minutos. Ele calcula todas as portas do host (HTTP, HTTPS e painel do Traefik, além do PostgreSQL) a partir de um checksum do prefixo do cliente, para que vários clientes possam compartilhar um host. Verifique se os deslocamentos de dois clientes não entram em conflito:

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

Como usar sua stack multi-tenant

Implantação de novos clientes

Com nosso script completo, criar um novo ambiente de cliente se torna simples:

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

Cada implantação cria:

  • Uma rede Docker dedicada com 16 contêineres
  • Volumes de dados separados para armazenamento persistente
  • Contêineres de serviços exclusivos com monitoramento de integridade
  • Uma configuração individual de túnel Cloudflare
  • Roteamento de domínios personalizados com certificados SSL
  • Uma infraestrutura de SSO de nível empresarial pronta para ativação

Gerenciamento de múltiplos ambientes

Monitore todos os ambientes de clientes em um ponto central:

# 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

Arquitetura dos contêineres em detalhes

Nossa arquitetura completa de 16 contêineres por cliente inclui:

Camada de infraestrutura (4 contêineres):

  • cloudflare-tunnel: Conectividade externa segura
  • traefik: Proxy reverso com SSL automático e descoberta de serviços
  • postgres: Banco de dados central com pool de conexões
  • whoami: Monitoramento de integridade e verificação do roteamento

Camada de aplicações (7 contêineres):

  • n8n: Automação de fluxos de trabalho com backend PostgreSQL
  • nocodb: Interface de banco de dados sem código
  • supabase-studio: Painel de desenvolvimento de backend
  • supabase-meta: Serviço de introspecção do banco de dados
  • supabase-auth: Autenticação e gerenciamento de usuários
  • supabase-rest: API REST gerada automaticamente
  • supabase-realtime: Assinaturas e atualizações em tempo real

Camada de IA e gateway (2 contêineres):

  • ollama: IA local com suporte a aceleração por GPU
  • supabase-kong: Gateway de API com limite de requisições e CORS

Camada de segurança empresarial (3 contêineres):

  • authentik-server: Servidor de autenticação SSO
  • authentik-worker: Tarefas em segundo plano e notificações
  • authentik-redis: Gerenciamento de sessões e cache

Dimensionamento dos recursos por cliente

Ajuste os recursos com base nas necessidades e nos padrões de uso de cada cliente:

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

Benefícios concretos para sua empresa

Isolamento completo entre clientes com recursos empresariais

Cada cliente recebe seu próprio ambiente completo, incluindo SSO de nível empresarial, recursos de IA e toda a infraestrutura de backend. Dados, configurações, personalizações e políticas de segurança permanecem separados. Um problema no nível da aplicação no ambiente de um cliente fica restrito àquele ambiente. Porém, como todos os clientes compartilham um host, problemas no nível do host, como o esgotamento dos recursos de CPU, memória ou disco, ainda podem afetar todos. Isso é semelhante ao isolamento que alcançamos com nossas implantações individuais do n8n.

Configuração rápida de novos clientes com todos os recursos

Novos clientes podem começar a usar uma stack completa de desenvolvimento e automação em menos de 10 minutos. O script de implantação automatiza a configuração, a geração de credenciais e a inicialização dos serviços; os registros DNS do Cloudflare e o SSO opcional continuam sendo etapas manuais breves, como mostram as instruções finais do script.

Custos previsíveis para recursos empresariais

Após a configuração inicial, não há custos de hospedagem por cliente além da infraestrutura básica. Ao contrário das soluções SaaS que cobram por usuário, por fluxo de trabalho ou por chamada de API, você paga um valor fixo pelo servidor e pode executar tantos ambientes de clientes com todos os recursos empresariais quanto os recursos dele permitirem.

Consistência profissional da marca

Cada cliente recebe domínios com sua própria marca e subdomínios profissionais (workflows.client.com, auth.client.com etc.) e pode personalizar completamente seus ambientes. Sem rodapés do tipo “fornecido por” ou interfaces compartilhadas que enfraqueçam a identidade da marca.

Integração com o n8n: automação de fluxos de trabalho empresariais em escala

É aqui que os recursos ganham força. Assim como mostramos como auto-hospedar o n8n para automação de fluxos de trabalho, esta configuração multi-tenant oferece a cada cliente sua própria instância completa do n8n, integrada a uma stack empresarial completa.

Cada cliente pode criar fluxos de trabalho sofisticados que:

  • Se conectam aos seus próprios bancos de dados (NocoDB, Supabase PostgreSQL)
  • Usam seus próprios modelos de IA (Ollama) para automação inteligente
  • Autenticam usuários por meio de SSO empresarial (Authentik)
  • Se integram às ferramentas de negócios e APIs específicas do cliente
  • Processam seus dados dentro do próprio ambiente isolado

Essa combinação cria uma plataforma poderosa de prestação de serviços aos clientes, na qual você pode:

  • Implantar rapidamente recursos padronizados de automação
  • Personalizar os fluxos de trabalho por cliente sem afetar os demais
  • Ampliar a prestação de serviços sem aumentos lineares de custos
  • Manter a soberania completa dos dados de cada cliente
  • Oferecer controle de acesso baseado em SSO como base para o trabalho de segurança e conformidade

Essa abordagem segue os mesmos princípios usados em nosso guia de configuração do Windmill com Docker, mas os estende a uma arquitetura multi-tenant completa.

Opções avançadas de configuração

Implementação de SSO empresarial com Authentik

Ative a autenticação única em todos os serviços do cliente configurando a autenticação delegada (forward authentication) do Authentik:

# 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

Em seguida, atualize os rótulos dos serviços para usar o middleware:

labels:
  - "traefik.http.routers.n8n.middlewares=auth-global"
  - "traefik.http.routers.nocodb.middlewares=auth-global"
  - "traefik.http.routers.supabase-studio.middlewares=auth-global"

Adição de um banco de dados vetorial para IA avançada

Amplie os recursos de IA com o banco de dados vetorial Qdrant:

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}"

Implementação de uma arquitetura híbrida de IA

Para obter o melhor desempenho, considere uma abordagem híbrida que combine IA em contêineres e IA executada nativamente:

# 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

No macOS, isso é relevante porque o Docker Desktop não oferece suporte a GPUs em contêineres, enquanto o Ollama executado nativamente usa a aceleração de GPU diretamente. Em nossa configuração, isso melhorou o desempenho em aproximadamente 5 a 6 vezes, enquanto os demais serviços continuaram em contêineres.

Stack de monitoramento e observabilidade

Adicione monitoramento completo por cliente:

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}"

Problemas comuns e soluções

Erros “Service Unavailable” ou HTTP 502

Geralmente, isso significa que o Traefik não consegue alcançar o contêiner de destino. Faça estas verificações:

# 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

Problemas de resolução de DNS

A configuração de DNS com curinga é essencial para o roteamento por subdomínios:

# 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

Esgotamento de recursos com múltiplos clientes

Monitore o uso de recursos em todos os ambientes de clientes:

# 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}}"

Esgotamento do pool de conexões do banco de dados

Com muitos clientes, os limites de conexões do PostgreSQL podem ser atingidos. Configure os parâmetros para cada implantação:

-- 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();

Problemas de configuração do SSO com Authentik

Problemas comuns de configuração do SSO e suas soluções:

# 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

Problemas de conexão do Cloudflare Tunnel

Investigue os problemas de conectividade do túnel:

# 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

Considerações sobre a infraestrutura

Dimensionamento da infraestrutura para múltiplos clientes

Para uma configuração típica que atende simultaneamente de 10 a 15 clientes com stacks completas de 16 contêineres:

Requisitos mínimos:

  • CPU: De 16 a 24 núcleos (2 núcleos por ambiente de cliente ativo)
  • RAM: De 64 a 128 GB (de 4 a 8 GB por cliente, dependendo do uso de IA)
  • Armazenamento: SSD NVMe com 2 TB ou mais (bancos de dados, modelos de IA e logs crescem com o tempo)
  • Rede: Conexão Gigabit para acesso dos clientes com boa capacidade de resposta

Recomendado para produção:

  • Servidor: Hetzner CCX62 ou similar (48 vCPU, 192 GB de RAM)
  • Armazenamento: 4 TB de NVMe com sistema automatizado de backup
  • Rede: Múltiplas conexões redundantes
  • Monitoramento: Stack completa de observabilidade com alertas

Estratégia de backup para ambientes com múltiplos clientes

Implemente backups automatizados por cliente:

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

Reforço de segurança para produção

Implemente um conjunto completo de boas práticas de segurança:

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

Aplique o middleware de segurança a todos os serviços dos clientes:

labels:
  - "traefik.http.routers.n8n.middlewares=security,ratelimit,${AUTH_MIDDLEWARE}"

Análise de custos: os números que importam

Custos de SaaS tradicionais (10 clientes empresariais com todos os recursos)

Custos mensais por cliente:

  • n8n Pro: $50/mês por cliente = $500/mês
  • Supabase Pro: $25/mês por cliente = $250/mês
  • Plataforma sem código (Airtable): $20/mês por cliente = $200/mês
  • SSO empresarial (Auth0): $23/mês por cliente = $230/mês
  • Custos de API de IA (OpenAI): $50/mês por cliente = $500/mês
  • Total: $1.680/mês = $20.160/ano

Custos da stack empresarial multi-tenant auto-hospedada

Custos anuais de infraestrutura:

  • Servidor em nuvem com vCPUs dedicadas (Hetzner CCX62): $350/mês = $4.200/ano
  • Custos de domínios (10 clientes): $120/ano
  • Cloudflare Pro (opcional): $240/ano
  • Total: $4.560/ano

Economia anual: $15.600 (redução de custos de 77%)

Esses valores refletem os preços de quando este guia foi publicado, em agosto de 2025. Em outubro de 2026, o plano B2C Essentials da Auth0 parte de $35/mês, e a Hetzner não lista mais o CCX62; seu sucessor, o CCX63, com 48 vCPUs e 192 GB, está listado por €853,99/mês, sem IVA, incluindo um endereço IPv4 primário. Recalcule os dois cenários com os preços atuais antes de decidir.

Além disso, você obtém:

  • Soberania e privacidade completas dos dados
  • Personalização e uso de marca própria sem limites
  • Ausência de dependência de fornecedor ou de limites de requisições dos planos SaaS
  • Controles de segurança e conformidade que você gerencia
  • Possibilidade de oferecer serviços de revenda
  • Controle total sobre atualizações e recursos

Isso é especialmente vantajoso quando se considera que nossa configuração oferece recursos empresariais que normalmente custariam muito mais em assinaturas SaaS, assim como os benefícios de custo demonstrados em nossa análise da auto-hospedagem do n8n.

Integração com WordPress: simplificação dos fluxos de trabalho de conteúdo

Para agências e equipes que gerenciam sites WordPress junto com seus ambientes de desenvolvimento, esta stack multi-tenant se integra muito bem aos fluxos de automação do WordPress. Assim como nosso plugin tva Duplicate Pro simplifica o gerenciamento de conteúdo no WordPress, este ambiente em contêineres pode automatizar fluxos de trabalho complexos entre sites WordPress e sua infraestrutura de desenvolvimento.

Possibilidades de integração com WordPress:

  • Distribuição de conteúdo: Fluxos de trabalho do n8n que enviam automaticamente conteúdo do WordPress para os sistemas dos clientes
  • Implantações automatizadas: Alterações nos sites WordPress acionam implantações nos ambientes dos clientes
  • Sincronização de dados: Alterações nos bancos de dados dos clientes (via NocoDB) atualizam automaticamente o conteúdo do WordPress
  • Conteúdo gerado por IA: Modelos do Ollama geram conteúdo que é enviado aos sites WordPress
  • Relatórios para clientes: Relatórios automatizados no WordPress gerados a partir de métricas dos ambientes de desenvolvimento

Isso cria um ecossistema completo no qual o gerenciamento de conteúdo do WordPress, os fluxos de desenvolvimento e a prestação de serviços aos clientes funcionam de forma integrada.

Vale a pena investir tempo nessa configuração?

Se você gerencia ambientes de desenvolvimento para múltiplos clientes, está criando um negócio SaaS ou administra uma agência que entrega soluções técnicas, sem dúvida. A configuração inicial leva cerca de um dia, mas ao final você obtém:

Benefícios imediatos:

  • Configuração inicial automatizada de clientes em menos de 10 minutos, com uma stack empresarial completa
  • Isolamento robusto entre os ambientes dos clientes, com identidade visual profissional
  • Grande economia de custos em comparação com serviços gerenciados (redução de 75% ou mais)
  • Controle total sobre dados, personalizações e conformidade
  • Arquitetura escalável que cresce com sua empresa
  • Controle de acesso baseado em SSO com Authentik

Valor a longo prazo:

  • Retenção de clientes por meio de uma prestação de serviços superior e apresentação profissional
  • Crescimento da receita pela capacidade de atender mais clientes com eficiência
  • Vantagem competitiva ao oferecer recursos empresariais a preços competitivos
  • Conhecimento técnico que diferencia sua empresa no mercado

Para agências que atendem múltiplos clientes ou startups SaaS que desejam manter o controle enquanto crescem, esta configuração oferece recursos de nível empresarial sem os custos correspondentes. A combinação de contêineres, implantação automatizada, roteamento adequado de domínios e segurança empresarial cria uma base para um crescimento significativo do negócio que permanece inteiramente sob seu controle.

A configuração se torna ainda mais valiosa quando você considera as possibilidades de integração com ferramentas existentes, como a automação do WordPress, e a estabilidade comprovada das plataformas de automação auto-hospedadas.

O que vem a seguir?

Estamos trabalhando ativamente em melhorias para esta arquitetura multi-tenant. Os próximos tutoriais abordarão:

Opções avançadas de implantação:

  • Guia de migração para Kubernetes para máxima escalabilidade e implantação empresarial
  • Gerenciamento automatizado de certificados SSL com fluxos de trabalho integrados do Let's Encrypt
  • Monitoramento e alertas avançados com Prometheus, Grafana e painéis personalizados
  • Automação da recuperação de desastres com estratégias de backup em múltiplas regiões

Melhorias na experiência do cliente:

  • Portal de autoatendimento para clientes para gerenciar seus próprios ambientes e configurações
  • Personalização com marca própria por meio de modelos com a identidade visual da agência
  • Modelos avançados de fluxos de trabalho para processos de negócios comuns
  • Guias de integração para ferramentas de negócios e APIs populares

Recursos empresariais:

  • Reforço avançado de segurança com WAF e detecção de intrusões
  • Estruturas de conformidade para GDPR, SOC2 e outras regulamentações
  • Implantação em múltiplas regiões com estratégias para bases globais de clientes
  • Guias de otimização de desempenho para ambientes com alto tráfego

O futuro da prestação de serviços aos clientes não está em escolher entre controle e conveniência, mas em criar sistemas que ofereçam ambos, cresçam com eficiência e mantenham padrões profissionais.

Obtenha suporte profissional

Configurar um ambiente multi-tenant com 16 serviços em contêineres envolve muitos componentes. Embora tenhamos fornecido documentação completa, cada empresa tem requisitos próprios e uma infraestrutura existente que precisa ser considerada.

Se você está implementando esta configuração para uso em produção ou precisa de personalizações para atender às necessidades específicas de seus clientes, nossa equipe pode ajudar com:

  • Estratégias de implantação personalizadas para sua infraestrutura
  • Integração com sistemas e fluxos de trabalho existentes
  • Otimização de desempenho para a carga específica de seus clientes
  • Reforço de segurança para atender a requisitos de conformidade
  • Treinamento da equipe para gerenciar ambientes multi-tenant
  • Estratégias contínuas de manutenção e monitoramento

Entre em contato pelo tva.sg para discutir suas necessidades de arquitetura multi-tenant e obter orientação profissional para a implementação.

Seja para ampliar uma agência existente, lançar uma nova plataforma SaaS ou desenvolver recursos de nível empresarial para atender seus clientes, estamos aqui para ajudar você a alcançar bons resultados com soluções auto-hospedadas em contêineres que preservam sua independência e entregam resultados profissionais.

Artigos relacionados

Leitura complementar