Como estabilizei uma VPS travando com SWAP lotado, Wazuh, kiro, codex e sessões SSH caindo
Voltar para blog

Como estabilizei uma VPS travando com SWAP lotado, Wazuh, kiro, codex e sessões SSH caindo

07/06/2026 · 13 min · Infraestrutura

Como estabilizei uma VPS travando com swap lotado, Wazuh, Kiro, Codex e sessões SSH caindo#

Fala, pessoal. Hoje o dia foi tenso. Eu estava no flow, codando um projeto que vou chamar aqui de Evolya, quando a conexão com o servidor começou a cair do nada. Não era só o editor remoto desconectando. A sensação era de que a VPS inteira resolvia tirar um cochilo a cada alguns minutos.

Eu estava usando dois editores remotos simultâneos via SSH, o Kiro e o Codex. A cada 10 minutos vinha o mesmo soco no estômago: conexão perdida, sessão travada, terminal sem resposta e aquela dúvida clássica de infraestrutura, "foi rede, foi SSH ou o servidor está morrendo por dentro?".

Nota explicativa: Kiro e Codex são editores de código com suporte a inteligência artificial (IA) que rodam processos no servidor remoto via protocolo SSH. Eles iniciam servidores de linguagem (Language Server Protocol - LSP), watchers de arquivos do sistema e processos auxiliares de indexação que consomem recursos de memória RAM e CPU significativos.

Como trabalho com suporte e infraestrutura há bastante tempo, esse tipo de sintoma acende um alerta específico. Queda recorrente de SSH durante carga de desenvolvimento remoto raramente é apenas "internet ruim". Muitas vezes é pressão de memória, swap saturado, I/O travado, processo Java consumindo heap, node indexando projeto em duplicidade ou kernel gastando energia tentando sobreviver.

Então eu parei de tratar como desconexão de editor e fiz a autópsia da VPS.

O sintoma operacional#

O padrão era este:

O projeto tinha muitos arquivos, dependências Node e atividade de linguagem. Isso importa porque editores remotos não são apenas "uma janela bonita". Eles sobem processos no servidor, indexam arquivos, rodam LSP, TypeScript Server, watchers, extensões, caches e tarefas auxiliares.

Com dois editores simultâneos, esse custo dobra ou quase dobra.

Diagnóstico forense: investigando o OOM killer#

Quando um servidor web ou VPS começa a apresentar travamentos intermitentes e lentidão extrema sob estresse de memória, o primeiro suspeito a ser investigado é o OOM (Out Of Memory) Killer do kernel do Linux. Ele atua eliminando processos pesados para salvar o sistema de um pânico geral.

Para auditar se o OOM Killer foi ativado e quais serviços foram sacrificados:

# Buscar registros de processos abortados por falta de memória no buffer de mensagens do kernel
dmesg | grep -i "oom\|out of memory"

# Investigar o log de sistema syslog
grep -i "oom\|out of memory" /var/log/syslog

# Verificar processos mortos via journalctl
journalctl -k | grep -i "oom\|killed process"

# Verificar se há processos no sistema em estado zumbi ou terminando
ps aux | grep -i "killed"

Auditoria de desempenho de disco (i/o bottlenecks)#

O travamento das sessões SSH muitas vezes decorre da contenção de I/O de disco (disk thrashing) durante as operações de swap agressivo. Para validar se o disco SSD/NVMe da VPS está saturado:

# Monitorar estatísticas estendidas de leitura e escrita do disco (atualizado a cada 1 segundo)
iostat -x 1 5

# Monitorar em tempo real quais processos estão gerando mais I/O de escrita e leitura
iotop -o

# Listar processos agrupados por consumo detalhado de I/O
sudo iotop -oP

# Verificar estatísticas de memória virtual e taxas de paginação (si/so - swap in / swap out)
vmstat 1 10

# Verificar se existem processos ativamente bloqueados esperando operações de I/O de disco
cat /proc/stat | grep procs_blocked

A primeira medição: memória e SWAP#

A VPS tinha 12GB de RAM. Em teoria, parecia folgado para desenvolvimento e serviços auxiliares. Mas quando rodei:

free -g

o resultado explicou muita coisa:

          total        used        free      shared  buff/cache   available
Mem:             11           7           2           0           1           3
Swap:             1           1           0

O ponto crítico estava no swap:

Swap: 1GB total, 1GB usado, 0GB livre

O swap estava 100% entupido.

Com 12GB de RAM, manter apenas 1GB de swap é muito apertado para um servidor que roda Wazuh/OpenSearch, processos Node de editores remotos e projeto grande sendo indexado. Quando a RAM começa a encostar no limite e o swap já está cheio, o Linux entra em um cenário muito ruim: ele tenta mover páginas de memória entre RAM e disco, mas não tem margem suficiente para respirar.

O que acontece quando o SWAP lota#

Quando o Linux fica sem memória física confortável e sem swap livre, ele pode entrar em comportamento de pressão extrema. Um dos efeitos práticos é o disk thrashing.

Na prática:

Abaixo está o fluxo visual da cascata de degradação sob pressão extrema de memória:

flowchart TD A["RAM Cheia (Pressão de Memória)"] --> B["Swap Ativo (Paginação)"] B --> C{"Swap Disponível?"} C -- "Não" --> D["OOM Killer Ativado (Abate Processos)"] C -- "Sim" --> E["I/O de Disco Intenso (Disk Thrashing)"] E --> F["CPU Wait Elevado (I/O Wait)"] F --> G["Processo sshd sem Tempo de CPU"] G --> H["Sessão SSH Travada / Queda de Conexão"] D --> H style A fill:#1e3a5f,stroke:#fff,stroke-width:2px,color:#fff style B fill:#78350f,stroke:#fff,stroke-width:2px,color:#fff style C fill:#78350f,stroke:#fff,stroke-width:2px,color:#fff style D fill:#7f1d1d,stroke:#fff,stroke-width:2px,color:#fff style E fill:#78350f,stroke:#fff,stroke-width:2px,color:#fff style F fill:#78350f,stroke:#fff,stroke-width:2px,color:#fff style G fill:#7f1d1d,stroke:#fff,stroke-width:2px,color:#fff style H fill:#7f1d1d,stroke:#fff,stroke-width:2px,color:#fff

Esse detalhe é importante. O SSH não precisa estar configurado errado para cair. Ele pode cair porque o servidor fica tempo demais sem conseguir responder. Se o processo sshd, o shell, o terminal remoto ou o processo do editor não conseguem agendar CPU e I/O a tempo, a conexão parece morrer.

Encontrando os culpados#

Depois da leitura de memória, fui procurar quem estava consumindo mais RAM.

Um comando prático para isso é:

ps aux --sort=-%mem | head -20

Também gosto de usar:

top

ou:

htop

quando disponível.

Os principais vilões eram previsíveis, mas a combinação deles era o problema real.

Wazuh indexer: java/opensearch comendo memória#

O Wazuh Indexer, baseado em OpenSearch/Java, estava consumindo quase 2GB de RAM sozinho. O heap estava configurado com 1GB fixo, mas o processo Java não vive apenas de heap. Existe overhead de JVM, threads, buffers, mmap, cache, estruturas internas e o próprio custo operacional do indexador.

Configurando o JVM heap do Wazuh indexer#

Para servidores de desenvolvimento compartilhados ou VPS com recursos limitados, reduzir a alocação máxima de memória heap da máquina virtual Java (JVM) é vital para evitar travamentos de outros processos essenciais.

  1. Abra o arquivo de opções de configuração da JVM do indexador:
sudo nano /etc/wazuh-indexer/jvm.options
  1. Reduza os limites mínimo (-Xms) e máximo (-Xmx) de memória alocada (por exemplo, definindo 512MB para ambientes pequenos):
-Xms512m
-Xmx512m
  1. Caso aplicável, revise parâmetros gerais de memória no arquivo de configuração do indexador:
sudo nano /etc/wazuh-indexer/opensearch.yml
  1. Reinicie o serviço do indexador para aplicar os novos limites:
sudo systemctl restart wazuh-indexer
  1. Verifique o consumo real de memória do processo após o reinício:
ps aux | grep wazuh | grep -v grep

Ferramentas como Wazuh são excelentes, mas em VPS pequena ou média elas precisam ser dimensionadas com carinho. O Java reserva memória com vontade, e o OpenSearch gosta de estabilidade, disco rápido e RAM previsível.

Em um servidor onde eu também estou desenvolvendo com editores remotos, o Wazuh não concorre apenas com serviços de produção. Ele concorre com:

Kiro e codex: Node.js e tsserver duplicados#

O segundo grupo de consumo vinha dos editores remotos. Kiro e Codex, usados via SSH, levantam processos no servidor. Dependendo do projeto, eles iniciam servidores de linguagem e indexadores que analisam a árvore de arquivos.

Em projeto Node/TypeScript, isso geralmente envolve:

Com dois editores abertos no mesmo projeto, eu tinha instâncias duplicadas fazendo trabalho parecido. O que parece "só abrir outro editor" vira custo real de memória e CPU.

Impondo limites de memória para processos Node.js#

Como os processos do Language Server (LSP) e indexadores do TypeScript Server rodando sob o Node.js podem se expandir indefinidamente e consumir toda a memória RAM, é prudente aplicar limites de tamanho máximo de memória (heap size) para a runtime do Node.

  1. Limitar o consumo máximo em variáveis de ambiente globais de sessão:
export NODE_OPTIONS="--max-old-space-size=2048" # Limita a 2GB
  1. Ou limitar individualmente ao disparar scripts ou serviços de background:
node --max-old-space-size=1024 app.js # Limita a 1GB
  1. Para ambientes multiusuários com isolamento estrito, configure grupos de controle do kernel (cgroups):
# Criar o cgroup de memória
sudo cgcreate -g memory:/node-limit

# Definir o teto de consumo de RAM do grupo para 2GB
sudo cgset -r memory.limit_in_bytes=2G node-limit

# Executar o processo restrito ao cgroup criado
sudo cgexec -g memory:node-limit node app.js

Esse foi o conselho de vilões do incidente:

Wazuh Indexer + Kiro + Codex + Node/TSServer + swap pequeno

Solução 1: Upgrade urgente do SWAP#

O primeiro passo foi dar fôlego para o servidor.

Um swap de 1GB para uma VPS com 12GB de RAM e esse perfil de carga era pouco. Eu subi para 8GB, usando arquivo de swap.

Backup de arquivos críticos do sistema#

Antes de editar qualquer ponto de montagem ou arquivos do kernel do Linux, faça backups de segurança. Um erro de sintaxe no arquivo /etc/fstab impedirá o boot do servidor remoto, resultando em lockout administrativo.

# Criar backup com timestamp do fstab
sudo cp /etc/fstab /etc/fstab.bak.$(date +%Y%m%d)

# Criar backup completo e compactado de todas as configurações críticas de sistema
sudo tar czf /root/system-backup-$(date +%Y%m%d).tar.gz /etc/fstab /etc/sysctl.conf /etc/ssh/sshd_config

Primeiro desativei o swap antigo:

sudo swapoff -a

Depois criei um novo arquivo de 8GB:

sudo fallocate -l 8G /swapfile_novo
sudo chmod 600 /swapfile_novo
sudo mkswap /swapfile_novo
sudo swapon /swapfile_novo

O chmod 600 é obrigatório por segurança. O arquivo de swap /swapfile_novo não deve ser legível por qualquer usuário, porque pode conter páginas de memória de processos.

Depois eu valido com:

free -h
swapon --show

O objetivo não é transformar swap em RAM principal. O objetivo é dar margem para o kernel sobreviver a picos sem travar a VPS.

Persistência do SWAP#

Para tornar permanente, eu valido a entrada no /etc/fstab. O formato típico seria:

/swapfile_novo none swap sw 0 0

Antes de reiniciar uma VPS remota, eu sempre valido sintaxe com cuidado, porque erro em /etc/fstab pode atrapalhar boot.

Também posso testar:

sudo mount -a
swapon --show

No caso de swap, o mais importante é garantir que ele suba após reboot e que o swap antigo não volte duplicado, se não for mais desejado.

Solução 2: Tunando swappiness#

Depois de criar swap, eu ajustei o comportamento do kernel.

Não adianta ter swap maior se o sistema usar disco de forma agressiva quando ainda existe RAM útil. Para esse tipo de VPS, eu prefiro o kernel usando swap como última linha de defesa, não como rotina.

Apliquei:

sudo sysctl vm.swappiness=10

E deixei permanente:

echo 'vm.swappiness=10' >> /etc/sysctl.conf

O vm.swappiness=10 orienta o kernel a priorizar RAM e só usar swap de forma mais conservadora. Não significa "nunca usar swap", mas reduz a tendência de paginar cedo demais.

Depois valido:

sysctl vm.swappiness

Cuidado com duplicidade no sysctl.conf#

Um detalhe operacional: antes de sair dando append no /etc/sysctl.conf, eu gosto de verificar se a diretiva já existe:

grep -n '^vm.swappiness' /etc/sysctl.conf

Se existir, eu edito a linha. Se não existir, adiciono. Isso evita múltiplas linhas conflitantes no arquivo.

Em ambiente controlado, também posso criar um arquivo dedicado em /etc/sysctl.d/99-vps-memory.conf com:

vm.swappiness=10

Mas no atendimento descrito eu usei o caminho direto no /etc/sysctl.conf, porque precisava estabilizar rápido.

Solução 3: Ajuste fino no SSH#

Com memória estabilizada, eu ajustei o SSH para ser mais tolerante a períodos curtos de carga e limitei conexões não autenticadas simultâneas.

Verificação de logs do SSH para depuração#

Para diagnosticar se conexões caíram por timeouts ou falhas de autenticação de sockets, analise os logs do daemon sshd:

# Verificar últimas mensagens do daemon SSH
sudo tail -100 /var/log/auth.log | grep sshd

# Buscar especificamente por mensagens de timeout, desconexões e resets de conexões
sudo grep -i "disconnect\|timeout\|reset" /var/log/auth.log | tail -20

Ajustando parâmetros no sshd_config#

No arquivo /etc/ssh/sshd_config usei:

TCPKeepAlive yes
ClientAliveInterval 60
ClientAliveCountMax 3
MaxSessions 50
MaxStartups 10:30:60

O que isso faz na prática:

Depois de editar, eu sempre valido a configuração antes de reiniciar:

sudo sshd -t

Se não houver erro, aplico:

sudo systemctl restart sshd

ou, dependendo da distro:

sudo systemctl restart ssh

Esse cuidado é importante porque erro no /etc/ssh/sshd_config em VPS remota pode te trancar fora do servidor.

Por que SSH keepalive não resolve SWAP lotado#

Esse ponto é importante. Ajustar SSH ajuda, mas não resolve servidor travado por memória.

Se a VPS entra em thrashing severo, o sshd pode nem conseguir responder aos keepalives. Então o ajuste de SSH é uma camada de tolerância, não a cura da causa raiz.

A ordem correta foi:

estabilizar memória -> ajustar comportamento do kernel -> ajustar SSH

Se eu fizesse só SSH, as quedas continuariam quando a VPS ficasse sem fôlego.

Limpando processos remotos zumbis#

Outra lição foi com os processos de editores remotos. Quando uma sessão cai, às vezes sobram processos no servidor.

Dependendo do editor, dá para encontrar processos como:

ps aux | grep -Ei 'vscode-server|node|tsserver|kiro|codex'

E, se necessário, limpar processos pendurados:

pkill -f vscode-server

ou com filtro específico para o processo problemático.

Eu uso isso com cuidado para não matar processo de build, aplicação Node legítima ou serviço em produção. O ideal é revisar antes com ps.

Inotify watches em projetos grandes#

Outro ponto importante para projetos com muitos arquivos é o limite de watchers do inotify.

Projetos Node com node_modules, monorepos, caches e muitos arquivos podem estourar limites do sistema. Editores remotos e ferramentas de build dependem de watchers para detectar mudanças em arquivos.

O ajuste emergencial é:

sudo sysctl -w fs.inotify.max_user_watches=524288

Para persistir, eu posso adicionar em sysctl:

fs.inotify.max_user_watches=524288

Esse ajuste não reduz consumo de RAM por si só, mas evita erros e loops estranhos de ferramentas tentando monitorar projeto grande sem limite suficiente.

Verificação e auditoria de limites do sistema#

Além dos watchers de arquivos, é fundamental verificar outros limites operacionais do sistema sob alta concorrência:

# Verificar limites de recursos atribuídos à sessão (ulimits)
ulimit -a

# Verificar o limite máximo de Process IDs (PIDs) permitidos no kernel
cat /proc/sys/kernel/pid_max

# Verificar o limite máximo de threads do sistema
cat /proc/sys/kernel/threads-max

# Verificar limite geral de descritores de arquivos abertos (file-max)
cat /proc/sys/fs/file-max

# Auditar configurações de overcommit de memória do kernel
cat /proc/sys/vm/overcommit_memory
cat /proc/sys/vm/overcommit_ratio

Ajuste de limites globais via systemd#

Para garantir que limites de conexões simultâneas e PIDs de serviços do sistema não travem processos sob cargas pesadas, edite as configurações do gerenciador do Systemd:

# Editar limites globais do systemd
sudo nano /etc/systemd/system.conf

# Adicionar ou descomentar as linhas de limites recomendados
# [Manager]
# DefaultLimitNOFILE=65536
# DefaultLimitNPROC=65536

# Recarregar as configurações de execução do Systemd sem reiniciar o servidor
sudo systemctl daemon-reexec

Verificação pós-reboot e monitoramento contínuo#

Sempre valide se todas as diretivas de otimização persistem após uma reinicialização da VPS.

Auditoria pós-reboot#

# 1. Verificar se o swapfile subiu corretamente e a quantidade de RAM livre
swapon --show
free -h

# 2. Confirmar se as configurações de swappiness e inotify persistiram
sysctl vm.swappiness
sysctl fs.inotify.max_user_watches

# 3. Testar se a sintaxe do SSH daemon está correta e keepalive está configurado
sshd -T | grep -i "keepalive\|session\|maxstartups"

# 4. Validar se o fstab não contém erros de sintaxe montando os pontos ativamente
sudo mount -a # Se o comando retornar sem saída de erro, está correto

Configurando o monitoramento contínuo de recursos#

Para garantir que a VPS permaneça estável sob picos de tráfego, implemente monitoramento de recursos em tempo real e de longo prazo:

# Monitorar estatísticas de consumo de RAM livre em tempo real
watch -n 1 free -h

# Acompanhar atividade de paginação de swap do sistema
watch -n 1 swapon --show

# Listar os 10 processos que mais consomem memória atualizado dinamicamente
watch -n 1 "ps aux --sort=-%mem | head -10"

# Instalar pacotes de monitoramento de performance essenciais
sudo apt install htop iotop sysstat -y

# Ativar e iniciar o daemon de monitoramento contínuo sysstat para histórico de logs
sudo systemctl enable sysstat
sudo systemctl start sysstat

O roteiro de guerra que usei#

Meu fluxo de estabilização ficou assim:

  1. Medir memória e swap:
free -g
swapon --show
  1. Identificar processos mais pesados:
ps aux --sort=-%mem | head -20
  1. Criar swap maior:
sudo swapoff -a
sudo fallocate -l 8G /swapfile_novo
sudo chmod 600 /swapfile_novo
sudo mkswap /swapfile_novo
sudo swapon /swapfile_novo
  1. Ajustar swappiness:
sudo sysctl vm.swappiness=10
echo 'vm.swappiness=10' >> /etc/sysctl.conf
  1. Ajustar SSH:
TCPKeepAlive yes
ClientAliveInterval 60
ClientAliveCountMax 3
MaxSessions 50
MaxStartups 10:30:60
  1. Validar SSH antes de reiniciar:
sudo sshd -t
  1. Aumentar inotify se necessário:
sudo sysctl -w fs.inotify.max_user_watches=524288
  1. Limpar processos remotos zumbis quando aplicável:
ps aux | grep -Ei 'vscode-server|node|tsserver|kiro|codex'
pkill -f vscode-server

Checklist: estabilização de VPS com problemas de memória#

Diagnóstico#

Correção - memória#

Correção - kernel#

Correção - SSH#

Pós-correção#

Resultado depois dos ajustes#

Depois dessas mudanças, a VPS parou de negar fogo. O uso de RAM estabilizou, o swap deixou de ficar completamente sem margem, e as sessões remotas pararam de cair em sequência.

O ponto principal é que eu não tratei a desconexão do SSH como causa. Eu tratei como sintoma. A causa estava na combinação de swap minúsculo, pressão de memória, Wazuh Indexer com Java/OpenSearch, editores remotos duplicando processos Node/TSServer e limites do sistema que não estavam dimensionados para aquele modo de trabalho.

No fim, a correção foi dar fôlego ao kernel, reduzir agressividade de swap, manter SSH mais tolerante e entender que dois editores remotos simultâneos têm custo real. É aquele tipo de ajuste que parece simples depois de pronto, mas que exige leitura operacional: medir, identificar gargalos e dimensionar a casa para o trabalho que ela precisa aguentar.

Este artigo foi útil?

Deixe uma reação rápida para apoiar o conteúdo:

CC BY-NC

Este post está licenciado sob CC BY-NC.

Comentários

Participe da discussão abaixo.

0 comentários