Executando verificação de segurança...
40

Sirvo 55 milhões de páginas em uma VPS de R$ 200/mês: o setup completo

Olá pessoal!

Seguindo a sequência de posts que venho fazendo nos últimos dias. Hoje falo sobre minha infra atual para rodar o cnpjaberto.com.br. No último (e penúltimo) post algumas pessoas vieram me perguntar qual era o custo de infra disso, e a respota é, bastante baixo! Mas isso só acontece devido a milhares de otimizações, truques que aprendi ao longo dos anos e outros que a IA (ela mesmo) acabou ensinando. Desfrutem!


Resumo da operação: 1 servidor, 4 containers Docker, 0 CDN, 0 load balancer, 0 serviço gerenciado. Hetzner CCX23 — 4 vCPU AMD dedicado, 16 GB RAM, 160 GB NVMe, 20 TB de tráfego. €29,74/mês líquido, uns R$ 200 dependendo do dólar. Acabo sempre arredondando para cima.

A máquina roda apenas o projeto cnpjaberto.com.br: um site para consultar o CNPJ de forma gratuita, os dados vem diretamente Receita Federal, hoje, o site possui ~70 milhões de estabelecimentos, ~67 milhões de empresas e ~27 milhões de sócios. 80 GB de dados ativos no Postgres, e cada estabelecimento ativo tem sua própria página com sitemap apontando, daí o "55 milhões de páginas" do título dos posts anteriores que postei aqui.

1. O hardware

Hetzner CCX23 (linha CCX é AMD dedicado, não a CX compartilhada, diferença concreta em paralelismo do Postgres):

  • 4 vCPU AMD dedicado
  • 16 GB RAM
  • 160 GB NVMe local
  • 20 TB de saída/mês (rede 1 Gbps)

Datacenter em Ashburn, US. Latência pro usuário brasileiro fica ~120ms. Isso complica um pouco, mas um servidor em SP ficaria muito mais caro e não quero gastar, ao menos agora.

2. Quatro containers, um host

  • postgres # 16-alpine, 80 GB de dados
  • redis # 7-alpine, 2GB maxmemory, allkeys-lru
  • backend # FastAPI, 4 workers uvicorn
  • frontend # Next.js 15, SSR sob demanda

Algumas pessoas comentaram em trocar o postgres pelo duckdb. Acabei não alterando pois o sistema já funciona bem com postgres, mas parece uma dica muito válida. Outro ponto: hoje mantenho o postgresql pois quero uma solução SAAS. O duckdb me parece uma solução de data lake ou analytics, me corrijam se estiver errado.

3 (gargalo). Postgres 80GB de dados, 16GB de RAM

Os parâmetros que importam do Postgres:

  • shared_buffers 3 GB # buffer interno (~20% da RAM)
  • effective_cache_size 9 GB # estimativa do que SO + PG cacheia
  • work_mem 16 MB # baixo de propósito (sort/hash por op)
  • maintenance_work_mem 1 GB # alto pra CREATE INDEX / VACUUM rápidos
  • random_page_cost 1.1 # NVMe ≈ leitura sequencial
  • max_wal_size 4 GB
  • jit off # planning alto, ganho marginal em OLTP

A parte crítica é shared_buffers + effective_cache_size + (work_mem × max_connections). Com 100 conexões, posso estourar 1.6 GB só em ordenação se o work_mem estiver alto. Por isso 16 MB e não os 64+ MB que tutoriais de internet
recomendam.

  • random_page_cost=1.1 foi a virada de chave pra fazer o planner usar índices em vez de scan sequencial em quase tudo, NVMe não tem latência rotacional (go SSDs!), a heurística default (4.0) está mentindo pra ele.

Os índices que mais carregam o trabalho:

  • B-tree em cnpj em estabelecimentos (lookup direto)
  • GIN com pg_trgm em razão social, fantasia e nome de sócio (busca fuzzy — escrevi um post sobre isso aqui também)
  • B-tree em cnae_principal, municipio, uf (filtros do panorama estatístico)

4. As seis camadas de cache

A maioria do tráfego não chega a tocar o Postgres em runtime. Sim.

CamadaOndeTTL
Lookup TablesRAM do processo Pythonaté restart (para sempre)
Detalhes CNPJRedis1h
Resultados de buscaRedis1h
Panorama (cnpjaberto.com.br/panorama)Snapshot postgres30d
Sitemap XMLunstable_cache30d
Bolsa/FIIRAM do Next7d

O redis usa allkeys-lru, ou seja, uma key mais recente invalida outras keys. Isso ajuda a controlar o espaço/tamanho do Redis.

5. Next.js sem SSG, SSR sob demanda

Já escrevi aqui um post inteiro sobre isso, então só o resumo: gerar 55 milhões de páginas estáticas em build time é inviável (tempo, disco, e o delta mensal da RFB destruiria qualquer estratégia de rebuild). A solução é SSR sob demanda unstable_cache por rota. Primeiro hit em página fria custa caro; segundo hit no mesmo CNPJ vem de cache em milissegundos.

6. Onde o sistema vai bater a parede

Sim, eu sei que isso vai acontecer.

Disco (160 GB): hoje ocupo ~110 GB. A base da RFB cresce ~2-3 GB/mês. Em 18 meses preciso particionar e arquivar histórico, ou subir pra CCX33 (240 GB).

CPU em ingestão: durante a ingestão mensal (2-4h) o Postgres consome os 4 vCPUs. A app fica lenta. Contornável rodando de madrugada, mas é um ponto real de contenção.

Quando bater na parede, o caminho é vertical: CCX33 custa €59/mês (8 vCPU, 32 GB, 240 GB) e provavelmente compra mais 12-18 meses. Sair pra arquitetura distribuída (réplica de leitura, CDN, object storage) só faz sentido depois de outro 10x de tráfego.

Por hoje era isso, obrigado pelo tempo de leitura :)


Menções honrrosas:

Todos que agregaram de alguma forma nos posts anteriores. Em especial ao @Detinho, que recomendou o uso de unstable cache.

Carregando publicação patrocinada...
9

Colirio para os olhos, relato real, sem ser escrito por IA, e sem teorismo de merda.

Obrigado por compartilhar, o fato de você ser pão duro com certeza levou a procurar formas de otimizar, 👏

1
1
2

Legal jovem!
esse tipo de relato eh o que queremos aqui,
tira uma duvida, pq tu usa SSR? nao eh mais barato computacionalmente soh servir o front e o client fazer a magica? deixa os caches na API e um abraco no gaiteiro...
outra coisa, acho q teus 4vcpu estao de boa, visto que soh fica lento no carregamento mensal, divide pra fazer parcial de madrugada.
a gente ficou muito mal acostumado com excesso de recurso e tanto app lixo usando 4gb, discord pra mandar msg, que se esquece q um trabalho bem feito de otimizacao e uso consciente de recurso faz literalmente magica.
eu nao subiria recurso, e espremeria o maximo do suco atual para aprendizado proprio;

dito isso:
parabens! fui compartilhar com a molier e ela falou que ja usava eauheau

1

Boa pergunta, justamente fiquei filosofando isso ontem. E honestamente, não sei te responder o motivo. Eu simplesmente assumo que fazer um cache de TUDO vai custar muito em espaço de disco, não sei se isso é um trade off válido. E também não tenho ideia de como calcular. Seria um cache de 70m paginas, muitas que nunca vão ser acessadas.

Mas entendo o ponto, o meu problema hoje é que uma pagina qualquer é cacheada no sistema, mas a chance de outra pessoa usar o mesmo cache é quase zero (ninguém vai chegar no MEI do zezinho...). Cachear uma pagina do nubakn, petrobras, até faria.

Sei lá, não sei te responder isso. Tem alguma ideia do sistema ideal? O site não é tão popular assim, está crescendo!

1
0
1

usa sse com json zstd e etag com cloudflare e você vai pagar 2.5usd em Miami via ipv6 rsrsrsrs.

Só aplicar essas técnicas ai e vai ser sucesso.

ps fiz o cadastro só pra te responder achei muito da hora o post parabéns mesmo!

1
1

parabens pelo post man, simples e claro, usando uma stack resumida e sem firulas.
falta mais disso no mercado, muita gente acaba acostumando a usar só soluções prontas e produtos de terceiros e esquece de aprender o básico de infraestrutura.

vi que indicaram migrar pra duckDB. Mas eu diria pra vc testar o clickhouse.
mas ai vai depender da natureza das consultas, porque clickhouse não é amigável com alterações de dados.
mas imagino que pra sua aplicação, um DB colunar atenda melhor.
fiz uma migração de um DB PostgreSQL pra clickhouse e consegui reduzir 5tb pra 300gb.
ate documentei isso aqui no tabnews na época, clickhouse tem uma compactação absurda e velocidade alta pra queries de agregação.

1

Vou dar uma lida, obrigado. No início do projeto tentei usar NoSQL, e, por mais que seja 100% a favor de usar NoSQL em qualquer projeto, nesse não funcionou. É muita correlação que indiretamente iria duplicar/triplicar o tamanho do banco. Especialmente no caso de "Empresas no meesmo endereço"

Exemplo petrobras: https://cnpjaberto.com.br/cnpj/33000167000101, no card principal na parte de endereço.

1
1
1

Muito obrigado! A motivação foi justamente por usabilidade. Passei muito tempo tentando encontrar informações nos sites existentes até decidir fazer o meu, abraços!

1
2
1

vamos considerar que o horário de concentração é das 8 horas comerciais. 400.000 / 8 = 50.000 por hora, 50.000 / 60 = 833 / minuto = 14 req / segundo. Quase nada de tráfego. Vc chegou a fazer um stress test? Eu uso o k6s, e no seu caso que será praticamente uma chamada por transação, vai ficar simples vc avaliar um capacity plan.

1

Eu uso uma vps com 16gb ram, 8núclios AMD 16 threads e 512gb de armasenamento.
Pago só 105 reais por mês.
Ainda vem com a possibilidade de vc colocar qualquer iso que quiser, tráfego ilimitado etc...
netcup
Eu recomendaria instalar o zram e o vram, criar um swap pra isso também.

1

Obrigado pela dica, não conhecia a netcup. A memória nesse momento não é gargalo, então tá safe por enquanto, mas vou ficar de olho, obrigado

1

Boa noite.
Primeiramente,obrigado pelo relato detalhado de sua solução .

Uma dúvida sobre o banco, vc disse qie está rodando o postgres em um container, como vc está realizando backup e se vc tem um plano de disaster recovery ?

0
1

Bom demais cara, só podia ser parente do homem aranha mesmo kkkkkkkkk

Não sei se você conhece, mas se precisar escalar isso e dependendo de como for escalar, pode fazer sentido usar o Kamal.

Parabéns pelo post e pelo trabalho, um abraço!

1
1
1

Chegou a testar o Alwaya Free Tier da Oracle?

Tem uma máquina 4 vCPU Ampere (processador Arm), 24GB de Ram e 200GB de armazenamento na faixa, precisa só se atentar ao tráfego pois não lembro de cabeça.

Com datacenter em Sao Paulo e Vinhedo, RS

1
1

Puts, que azar, você tinha algum serviço rodando? Eles tem uma política para um mínimo de utilização de recursos.

Recuperação de Instâncias de Computação Inativas

As instâncias de computação Always Free inativas podem ser recuperadas pela Oracle. Serão consideradas inativas pela Oracle as instâncias de computação de máquina virtual e bare metal se, durante um período de 7 dias, as seguintes afirmações forem verdadeiras:

A utilização de CPU para o percentil 95 é inferior a 20%
A utilização da rede é inferior a 20%
A utilização de memória é inferior a 20% (só se aplica a formas A1)

https://docs.oracle.com/pt-br/iaas/Content/FreeTier/freetier_topic-Always_Free_Resources.htm

Tenho minha VPS lá a uns 3 anos e nunca tive problema (tenho um servidor de Minecraft rodando inclusive rsrs)

1

Tinha um ambiente de teste de uma aplicação que seria migrada pra lá. Coisa de um dia pro outro eles removeram a instância, suporte via chat não trouxe nenhuma justificativa, só disse que realmente a instância foi removida e encerrou a conversa.

1

Lindo seu sistema, bem rápido e excelente sua ideia de compartilhar sua experiência. E também serve como divulgação do seu Saas. Só um feedback, a página da petrobras quando abro, aquela da página inicial, ela buga, acho que tem haver com o limite rate, so que agora ela não normaliza mais. Entrei numas 3 páginas pra testar e quando entro nela ela fica sem css. Mesmo depois de horas sem entrar ela continua sem css

1
0
1

Conta PRO, R$19/mes mas acho que vou precisar aumentar o valor para não morrer afogado. Hoje não tenho assinantes PRO, algo que contribui para a métrica.

1
1

SIm, meu comentário foi contra intuitivo. Assumo que as pessoas não comprem PRO simplesmente porque não conhecem a aplicação, e não pelo preço!