Um time de plataforma de uma pessoa só
TL;DR
- Contexto: SaaS automotivo B2B2C, lean e bootstrapped. Time de tecnologia: 1 dev de produto e eu, dono de 100% da plataforma e da infraestrutura.
- Escala: 60+ tenants em produção (≈200 containers), dois data centers em hot-standby, alvo de quatro noves.
- A tese: uma pessoa opera isso não por heroísmo, mas por arquitetura: tudo declarativo, tudo observável, tudo com caminho de volta.
O contexto: quando o time de plataforma é você
Num SaaS bootstrapped não existe "abre um ticket pra infra". Eu sou a infra, e também o SRE, o DBA, o time de segurança e o plantão. Isso muda o critério de toda decisão técnica: cada componente que entra na arquitetura é um componente que eu vou fazer patch, monitorar, e pelo qual vou ser acordado.
A disciplina que emerge disso tem nome: superfície mínima. Não é minimalismo estético. É sobrevivência. As seções abaixo são a mesma decisão aplicada em camadas diferentes.
A forma do problema: 60+ tenants, ≈200 containers
Cada tenant (uma revenda com seu próprio domínio) é materializado como um conjunto próprio de containers (site, API, backoffice), todos gerados do mesmo template versionado, sobre um plano de dados compartilhado (PostgreSQL + MongoDB) com isolamento por tenant na camada de aplicação. O roteamento é por domínio, no reverse proxy, e o DNS da frota inteira é reconciliado automaticamente contra um registro-âncora: tenant novo entra num manifesto, e o resto acontece.
O trade-off é consciente: containers por tenant custam mais memória que um monolito multi-tenant puro, mas compram isolamento de falha (um tenant enlouquecido não derruba os vizinhos), deploy e rollback por tenant, uma frota que um humano consegue raciocinar a respeito. E simplicidade no edge: cada revenda usa o próprio domínio comercial apontando direto para o seu conjunto de containers, sem uma camada complexa de proxy e rewrite para multiplexar um app único entre dezenas de domínios de terceiros.
Alta disponibilidade que não depende de mim acordado
A produção roda em dois data centers geo-separados em hot-standby: PostgreSQL com Patroni (auto-failover), MongoDB replica sets com árbitro num terceiro ponto neutro, Redis Sentinel, réplicas assíncronas com RPO de segundos. Na frente, um load balancer cujo health check só responde 200 no data center onde o banco é o líder gravável: o tráfego segue o failover do banco sozinho, com detecção em ~1–2 minutos, sem intervenção humana.
A história completa de como a produção chegou nesses dois DCs, com zero downtime, é o case da migração. Aqui, o ponto é outro: failover automático é o que permite um time de um dormir.
A plataforma interna: GitOps sem cerimônia
O deploy da frota é governado por GitOps, via um control-plane leve, ferramenta open-source adotada de propósito (manager central + agentes por VM, rede zero-trust). Superfície mínima vale também pra isso: não reinvento commodity; a engenharia própria vai onde ferramenta pronta não resolve (o manifesto que materializa a frota, o reconciliador de DNS, o pré-aquecedor de failover, a observabilidade sob medida):
- Desired state no git. Deploy é commit + push; o control-plane só aplica o que tem commit novo. Drift detection compara o que roda com o que o git diz que deveria rodar.
- Adoção sem recriação: containers existentes podem ser adotados pelo estado declarado sem downtime.
- O control-plane não gerencia a si mesmo. Guard explícito: as peças que o sustentam (o próprio control-plane, o edge) só sobem por acesso direto, porque a ferramenta que resolve seu problema não pode depender de si mesma pra ser consertada.
- Secrets fora do git: gerenciador self-hosted, com identidades de máquina least-privilege por VM.
Antes disso havia uma UI de gestão de containers apontando e clicando. Substituí porque UI não tem diff, não tem history, não tem revert. Git tem os três de graça.
CI no GitHub Actions (builds multi-arch), imagens em registry próprio, e a frota de tenants provisionada por manifesto + scripts versionados com timer: tenant novo vira uma entrada num arquivo, não um procedimento.
Por que NÃO Kubernetes (ainda)
A pergunta que todo mundo faz. A resposta curta: Kubernetes resolve problemas de escala de organização que eu não tenho, a um custo de operação que eu pagaria sozinho.
A versão longa: o que eu preciso de um orquestrador é desired state, reconcile, drift detection, rollback e roteamento. E isso o control-plane GitOps + Docker + reverse proxy entregam com uma fração das partes móveis. O plano de controle do Kubernetes é, ele próprio, um sistema distribuído que alguém precisa operar, atualizar e entender às 3h da manhã. Aqui, esse alguém tem nome e sobrenome. Minha frota é estável e previsível (tenants não têm elasticidade de Black Friday), então scheduling dinâmico e autoscaling (o coração do valor do K8s) ficariam ociosos.
E os problemas realmente difíceis dessa operação (failover de banco cross-DC, replicação, cutover de DNS/LB, backup imutável) vivem abaixo e acima do orquestrador. Kubernetes não resolveria nenhum deles por mim; eu só passaria a operá-los e ao Kubernetes.
O "(ainda)" é honesto: em escala maior, ou com um time, a conta muda. Conhecer exatamente qual problema o K8s me resolveria (porque resolvi cada um na unha) é justamente o que me faz saber quando adotá-lo.
Pelo mesmo princípio, sem Terraform e sem Ansible: as VMs nascem de template cloud-init + scripts versionados no repo, direto na API do hypervisor. Para 2 hypervisors e uma dúzia de VMs, uma camada declarativa extra é mais superfície pra manter. A decisão registrada em ADR foi "uma forma só de gerenciar, menos superfície". IaC é o princípio; a ferramenta se escolhe pelo tamanho do problema.
Observabilidade: os olhos que substituem um time
Aqui foi o inverso do control-plane: construída sob medida, porque observabilidade genérica não sabe o que é um tenant. Se uma pessoa opera a frota, a frota precisa se reportar sozinha:
- Métricas de toda a frota (hosts, containers, Postgres/Patroni, Mongo, Redis, connection pooling), com meses de retenção e ~100 mil séries ativas.
- 180+ probes externos batendo nos sites dos tenants de fora: a disponibilidade medida é a que o cliente vê, não a que o servidor acha.
- Logs estruturados da frota inteira num pipeline central: milhões de linhas/dia com classificação de severidade.
- 30+ regras de alerta com roteamento por criticidade (crítico acorda; o resto espera o café) e deadman switches cruzados: o sistema que monitora é monitorado por outro. Quem vigia o vigia é uma pergunta com resposta escrita.
E a lista de probes se gera sozinha do manifesto de tenants: tenant novo entra no monitoramento sem ninguém lembrar de adicioná-lo. Automação que depende de memória humana não é automação.
Backup em camadas (porque réplica não é backup)
Réplica assíncrona protege contra perda de hardware. E replica fielmente o seu
DELETE errado. Por isso, camadas independentes:
- Réplica viva cross-DC: RPO de segundos, para perda de sítio.
- Snapshots ZFS a cada 15 minutos: para o "ops" de agora há pouco, rollback instantâneo.
- Backup diário cifrado das VMs inteiras, deduplicado, com restore testado e medido (centenas de GB restauradas em ~20 minutos).
- Off-site em dois destinos independentes, outro data center e outro provedor, este último com object-lock imutável: nem ransomware com as minhas credenciais apaga. E o banco de receita tem dump lógico de hora em hora: o RPO de catástrofe caiu de 24h para 1h.
A conta no fim do mês
Tudo isso (dois data centers, HA real, observabilidade, backups imutáveis) roda em dois servidores bare-metal e um punhado de serviços gerenciados baratos, por uma fração do que a mesma pegada custaria em cloud gerenciada. Hot-standby em vez de ativo-ativo foi decisão de custo consciente: metade do hardware "parado" é o prêmio do seguro, e ainda assim sobrou headroom pra frota dobrar.
Cost optimization aqui não é FinOps de dashboard: é arquitetura que cabe no negócio.
Lições
- Superfície mínima é uma estratégia, não uma estética. Cada componente a menos é um patch, um alerta e um modo de falha a menos. Escolha tecnologia chata e opere-a bem.
- Automação como sobrevivência. Um time de um não escala por esforço; escala por não precisar estar presente. Se um processo depende de memória humana, ele já falhou; só ainda não avisou. (O corolário: quando 200 containers resolveram reiniciar ao mesmo tempo depois de um reboot e afogaram os bancos, a resposta não foi "reiniciar com cuidado da próxima vez", e sim um bring-up escalonado e gated por health check, no boot, escrito uma vez.)
- Ownership total ensina o que abstração esconde. Resolver failover, replicação e DR na unha é o que me diz exatamente o que eu estaria comprando de um orquestrador ou de uma cloud, e quando vale a pena comprar.
- Trate o lab como produção e a produção como código. O padrão de operação que roda o SaaS (git como fonte de verdade, agentes leves, zero-trust) foi provado primeiro no meu homelab: 12 anos de laboratório viram método, não hobby.
Outros cases
Migrando 50+ tenants em produção entre data centers, com zero downtime Réplica quente, cutover atômico, e prova por captura de pacotes, não por otimismo. 12 anos de homelab: meu stack de IA self-hosted LLM local em GPU, RAG híbrido, e um agente que pentesta o meu próprio SaaS.Sempre aberto a trocar ideia com quem opera infraestrutura de verdade.
LinkedIn