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:
- O cliente acessa seu domínio personalizado (por exemplo,
workflows.client-a.com) - O Cloudflare Tunnel encaminha a requisição para sua instância local do Traefik
- 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
- O Authentik gerencia a autenticação única (SSO) em todos os serviços, se estiver configurado
- O cliente acessa seu próprio ambiente isolado, com seus dados e configurações
- 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 seguratraefik: Proxy reverso com SSL automático e descoberta de serviçospostgres: Banco de dados central com pool de conexõeswhoami: Monitoramento de integridade e verificação do roteamento
Camada de aplicações (7 contêineres):
n8n: Automação de fluxos de trabalho com backend PostgreSQLnocodb: Interface de banco de dados sem códigosupabase-studio: Painel de desenvolvimento de backendsupabase-meta: Serviço de introspecção do banco de dadossupabase-auth: Autenticação e gerenciamento de usuáriossupabase-rest: API REST gerada automaticamentesupabase-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 GPUsupabase-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 SSOauthentik-worker: Tarefas em segundo plano e notificaçõesauthentik-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.