Quando e como escalar a VPS

A pergunta não é se você vai precisar escalar, é quando. Escalar no momento certo, da forma certa, evita tanto o subdimensionamento (servidor caindo em picos) quanto o superdimensionamento (pagando por recursos ociosos). Este guia mostra como identificar os gargalos reais e escolher a estratégia correta.

Monitorar para saber quando escalar

Métricas que indicam necessidade de escalonamento:

bash
# CPU — sinal de alerta quando > 70% por longos períodos:
watch -n 2 "top -bn1 | grep 'Cpu(s)' | awk '{print $2}'"

# Memória — problema quando swap está em uso constante:
free -h
vmstat 1 10 | awk '{print $3, $4, $5, $6}'  # swap in/out

# Disco — I/O wait > 10% indica gargalo de disco:
iostat -x 2 | grep -v "loop|dm-"

# Load average — preocupante quando > número de CPUs:
uptime    # ex: load average: 3.5 — problemático em VPS de 2 CPUs

# Rede — throughput e pacotes descartados:
sar -n DEV 1 5

# Script de alerta automático:
#!/bin/bash
CPU=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d. -f1)
MEM=$(free | awk '/Mem:/ {printf "%.0f", $3/$2*100}')
LOAD=$(cat /proc/loadavg | awk '{print $1}')
CPUS=$(nproc)

if [ "$CPU" -gt 80 ] || [ "$MEM" -gt 90 ]; then
    curl -s -X POST "https://api.telegram.org/botTOKEN/sendMessage" \
      -d "chat_id=CHAT_ID" \
      -d "text=⚠️ VPS sobrecarregada! CPU: ${CPU}% MEM: ${MEM}% Load: ${LOAD}/${CPUS}"
fi

Escalonamento vertical: upgrade da VPS

Quando e como aumentar os recursos da VPS atual:

bash
# Escalonamento vertical: mais CPU, mais RAM, mais disco
# Vantagem: simples, sem mudança de arquitetura
# Desvantagem: tem limite e fica caro; downtime durante o resize

# Checklist ANTES de fazer upgrade:
# □ Otimizei o código? (queries lentas, N+1, memory leaks)
# □ Tenho cache Redis configurado? (elimina requests ao banco)
# □ PM2 cluster usando todos os CPUs atuais?
# □ Linux sysctl otimizado? (file descriptors, TCP)
# □ Imagens Docker otimizadas? (menos memória)

# Quando vertical faz sentido:
# - CPU consistentemente > 70% com código otimizado
# - Memória insuficiente para o banco de dados (PostgreSQL shared_buffers)
# - Picos de tráfego previsíveis e curtos

# Redimensionar VPS na Runstack:
# Painel → VPS → Resize → escolher novo plano → Confirm
# (downtime de ~2-5 minutos)

# Após o resize — verificar que tudo subiu corretamente:
sudo systemctl status nginx postgresql docker
docker compose ps
pm2 list

Escalonamento horizontal: múltiplas VPSs

Arquitetura com load balancer e servidores de aplicação separados:

nginx
# Arquitetura horizontal:
# [Cloudflare/Nginx LB] → [App VPS 1] → [DB VPS]
#                       → [App VPS 2] ↗
#                       → [App VPS 3] ↗

# Pré-requisitos para escalar horizontalmente:
# 1. Aplicação deve ser STATELESS (sem sessão em memória local)
# 2. Sessões em Redis compartilhado
# 3. Uploads em MinIO/S3 (não disco local)
# 4. Logs centralizados (não arquivo local)

# Nginx como load balancer entre VPSs (no LB VPS):
upstream api_cluster {
    least_conn;                          # enviar para menos ocupado
    server 10.0.0.2:3000 weight=1;
    server 10.0.0.3:3000 weight=1;
    server 10.0.0.4:3000 weight=1;
    keepalive 32;
}

server {
    listen 443 ssl http2;
    location /api/ {
        proxy_pass http://api_cluster;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

# Health check para remover VPS com problema:
upstream api_cluster {
    server 10.0.0.2:3000;
    server 10.0.0.3:3000;
    check interval=3000 rise=2 fall=3 timeout=1000 type=http;
    check_http_send "GET /health HTTP/1.0

";
    check_http_expect_alive http_2xx;
}

Separar banco de dados da aplicação

Mover PostgreSQL para VPS dedicada é o primeiro passo horizontal:

bash
# Arquitetura recomendada para produção com tráfego médio:
# VPS App (2-4 CPU, 4 GB RAM): Node.js + Nginx + Redis
# VPS DB  (2-4 CPU, 8 GB RAM): PostgreSQL + backups

# Configurar PostgreSQL na VPS de banco para aceitar conexões remotas:
# /etc/postgresql/16/main/postgresql.conf:
listen_addresses = '10.0.0.5'   # IP privado da VPS DB (não 0.0.0.0)

# /etc/postgresql/16/main/pg_hba.conf — aceitar da VPS App:
host  minha_db  minha_app  10.0.0.2/32  scram-sha-256

# UFW na VPS DB — apenas VPS App pode se conectar:
sudo ufw allow from 10.0.0.2 to any port 5432 proto tcp
sudo ufw deny 5432/tcp

# Conexão da aplicação — usar IP privado da VPS DB:
DATABASE_URL=postgresql://app_user:senha@10.0.0.5:5432/minha_db

# PgBouncer na VPS App para pooling de conexões:
# docker run -d --name pgbouncer \
#   -e DB_HOST=10.0.0.5 \
#   -e DB_USER=app_user \
#   -e DB_PASSWORD=senha \
#   -e POOL_MODE=transaction \
#   edoburu/pgbouncer

# Monitorar latência entre App e DB:
pg_bench -h 10.0.0.5 -U app_user -d minha_db -c 10 -j 2 -T 30

Checklist de escalabilidade antes de escalar

Otimizações de baixo custo antes de pagar por mais hardware:

bash
# ── Nível 1: Otimizações gratuitas (fazer primeiro) ─────────────────
# □ EXPLAIN ANALYZE nas queries mais lentas — adicionar índices faltando
# □ PM2 cluster mode ativo — aproveitando todos os CPUs
# □ Redis cache para queries repetidas (hit rate > 90%)
# □ Compressão gzip no Nginx ativa
# □ Assets estáticos via CDN (Cloudflare)
# □ Connection pooling (PgBouncer) para PostgreSQL
# □ sysctl otimizado (file descriptors, TCP buffers, BBR)

# ── Nível 2: Escalonamento vertical (upgrade de plano) ──────────────
# □ CPU > 70% continuamente após otimizações → mais vCPUs
# □ RAM em swap continuamente → mais memória
# □ I/O wait > 10% → VPS com NVMe SSD ou separar banco

# ── Nível 3: Escalonamento horizontal ────────────────────────────────
# □ Aplicação stateless? (sessions em Redis, uploads em S3/MinIO)
# □ Banco separado em VPS dedicada
# □ Load balancer configurado (Nginx ou Cloudflare)
# □ Health checks nos servidores de aplicação
# □ CI/CD que faz deploy simultâneo em todos os servidores
# □ Monitoramento centralizado (Grafana vendo todos os servidores)

# Métricas para decidir quando escalar horizontal vs vertical:
# CPU bound → mais vCPUs (vertical até 8-16 cores, depois horizontal)
# Memory bound → mais RAM (vertical — geralmente mais barato)
# I/O bound → separar banco ou usar disco mais rápido
# Network bound → CDN + horizontal

Não quer configurar manualmente?

Implante o VPS em menos de 5 minutos com a Runstack. Infraestrutura da OPEN Datacenter, com servidores em São Paulo.

Perguntas frequentes

Quando você esgotou as otimizações de código e uma única VPS de 8-16 CPUs ainda não suporta a carga. Na prática: a maioria das aplicações chega longe com uma VPS bem otimizada. Escalonamento horizontal faz sentido quando você precisa de alta disponibilidade (zero downtime durante falha) além de performance.

Depende do provedor. Na maioria das VPS: resize requer parar a VM, downtime de 2-5 minutos. Para zero downtime: configure blue-green deploy (duas VPSs, uma nova com mais recursos, mudar o DNS/LB para a nova). Provedores como AWS EC2 permitem resize sem parar em alguns tipos de instância.

Depende da aplicação. Se você armazena sessões em memória local (express-session sem store externo): sim, Redis é obrigatório antes de escalar. Se a API é stateless com JWT: não precisa de Redis para sessão, mas provavelmente vai precisar para rate limiting distribuído e cache. Redis é o primeiro serviço compartilhado a configurar antes do escalonamento horizontal.

Duas VPSs de 4 GB RAM custam aproximadamente o mesmo que uma de 8 GB RAM, mas com a vantagem de alta disponibilidade e a desvantagem de complexidade operacional maior. Para a maioria dos casos: uma VPS maior é mais simples e igualmente econômica. Duas VPSs fazem sentido quando a disponibilidade (SLA) é critério de negócio.

GitHub Actions com matrix strategy: jobs.deploy.strategy.matrix.server: [vps1, vps2, vps3] executa o deploy em paralelo em todos. Alternativa: Fabric ou Ansible para deploy orquestrado. O processo: (1) build da imagem Docker, (2) push para registry, (3) SSH em cada VPS e docker compose pull + up -d. Com zero downtime: rolling update, atualizar um servidor por vez.