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 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 — 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 e 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 não é um procedimento, é uma entrada num arquivo.
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ã — e 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 — e 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" — foi 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