Criar swap e evitar falhas por falta de memória
Serviço em baixo, sem relatório de falha e com o log cortado a meio da frase: assim demonstra um OOM-Kill, cria corretamente um ficheiro de swap e reconhece quando o swap apenas adia o problema.
O serviço desapareceu. Não há relatório de falha, não há stacktrace, o ficheiro de log termina a meio de uma linha. O MariaDB deixou de responder, o nginx devolve 502, o servidor de Minecraft está offline e na própria aplicação não se vê nada de invulgar. O padrão é quase sempre o mesmo: o kernel ficou sem memória livre e terminou um processo para manter o sistema vivo. Este artigo mostra como demonstrar isso sem margem para dúvidas, como criar um ficheiro de swap corretamente e registá-lo de forma permanente, e em que casos o swap apenas adia o problema uns minutos.
Reunir a prova: dmesg e journalctl
Antes de configurar seja o que for, precisa da prova. A cada OOM-Kill (Out of Memory), o kernel escreve um bloco detalhado no buffer circular:
dmesg -T | grep -iE 'out of memory|oom-kill'
Se não vier nenhuma linha e o comando terminar com o código de saída 1, não houve nenhum OOM do kernel desde o último arranque do sistema. Esse código de saída não é um erro, é apenas a resposta habitual do grep quando não encontra nada. Se vier alguma coisa, o aspeto é este nos quatro sistemas aqui tratados:
mariadbd invoked oom-killer: gfp_mask=0x140cca(GFP_HIGHUSER_MOVABLE|__GFP_COMP), order=0, oom_score_adj=0
oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0,global_oom,task_memcg=/system.slice/mariadb.service,task=mariadbd,pid=1043,uid=107
Out of memory: Killed process 1043 (mariadbd) total-vm:2894760kB, anon-rss:1583204kB, file-rss:0kB, shmem-rss:0kB, UID:107 pgtables:3820kB oom_score_adj:0
Há três pormenores importantes. Primeiro: invoked oom-killer nomeia o processo que fez o pedido de memória, não necessariamente o culpado. Segundo: o processo terminado aparece na linha Killed process. Terceiro: o que conta é o anon-rss, ou seja, a memória anónima realmente ocupada. O total-vm é espaço de endereçamento reservado e, com Java ou Go, costuma ser um múltiplo daquele valor, pelo que não diz nada.
Se executar o dmesg sem ser root, o Debian 12, o Debian 13, o Ubuntu 22.04 e o Ubuntu 24.04 respondem com dmesg: read kernel buffer failed: Operation not permitted, porque kernel.dmesg_restrict está a 1 em todos eles. Trabalhe portanto com sudo ou como root. Se a mensagem se mantiver mesmo como root, está dentro de um container LXC ou OpenVZ que não tem acesso ao buffer circular do sistema anfitrião. Nesse caso só o segundo caminho, através do journalctl -k, o leva mais longe.
Depois de um reinício, o buffer circular está vazio. Daí o segundo caminho, através do journal, e é aqui que está a armadilha que a maioria dos guias ignora:
journalctl -k --grep "Out of memory"
O -k implica -b e mostra, por isso, exclusivamente o arranque atual. Se a máquina foi reiniciada depois do incidente, este comando não devolve nada, apesar de o acontecimento ter sido registado. Para o arranque anterior ou para um determinado período:
journalctl -k -b -1 --grep "Out of memory"
journalctl --since "7 days ago" --grep "Out of memory|oom-kill"
Se o primeiro dos dois comandos responder No journal boot entry found for the specified boot (-1), é porque no journal simplesmente não está guardado nenhum arranque anterior. Também isso não é um erro, é o caso normal num sistema cujo journal só regista desde o arranque atual.
Isto só funciona se o journal for de facto persistente. Verifique:
ls -d /var/log/journal
No Debian e no Ubuntu este diretório existe de origem, pelo que o teste dá quase sempre resultado e o mkdir seguinte fica sem efeito, sem estragar nada. Se, excecionalmente, o diretório não existir, o journal fica apenas em /run e perde-se a cada reinício. Isso recupera-se com dois comandos:
mkdir -p /var/log/journal
systemctl restart systemd-journald
OOM do kernel ou limite do systemd? Duas causas diferentes
Esta distinção decide se o swap chega sequer a servir para alguma coisa. Repare no campo constraint da mensagem do kernel.
CONSTRAINT_NONE significa: o sistema inteiro ficou sem memória. Aqui o swap ajuda.
CONSTRAINT_MEMCG significa: apenas um control group ultrapassou o seu limite, o resto do sistema tinha memória de sobra. Aqui o swap não ajuda, aqui é preciso aumentar o limite ou tornar a aplicação mais poupada. Estas mortes também se reconhecem no próprio serviço:
systemctl status mariadb
Se ali aparecer Main process exited, code=killed, status=9/KILL e, mais abaixo, Failed with result 'oom-kill', foi um OOM-Kill. Se houve ou não um limite de cgroup em jogo, revela-o um ficheiro de contadores. Ele pressupõe o cgroup v2, que é o padrão nas quatro distribuições; com o antigo cgroup v1 o caminho não existe:
cat /sys/fs/cgroup/system.slice/mariadb.service/memory.events
systemctl show mariadb -p MemoryMax -p MemoryHigh
Um valor maior do que zero em oom_kill, em conjunto com um MemoryMax definido, é a prova de um limite local.
Há ainda um terceiro candidato, muitas vezes esquecido porque não escreve absolutamente nada no dmesg: o systemd-oomd. Este serviço trabalha em espaço de utilizador, avalia o indicador de pressão PSI e termina control groups inteiros antes de o kernel sequer intervir. A sua mensagem no journal diz, no essencial, Killed /system.slice/... due to memory pressure for /system.slice being 60.00% > 50.00% for > 20s with reclaim activity. Verifique se está a correr:
systemctl is-active systemd-oomd
No Ubuntu, o systemd-oomd vem instalado e ativo de origem desde a 22.04; no Debian não faz parte da instalação padrão. Um inactive num servidor Debian é portanto expectável e não indica qualquer erro.
Importante para mais à frente: o systemd-oomd também pode disparar porque o swap está a encher. Com o oomd ativo, mais swap pode portanto provocar as interrupções mais cedo em vez de mais tarde.
Antes do swap: quanta memória falta realmente
Dois minutos de medição poupam uma decisão errada.
free -h
Só interessa a coluna available, não a free. A cache conta como disponível e é libertada quando for preciso. Quem lê a coluna free julga que qualquer sistema saudável está sobrecarregado.
ps -eo pid,comm,rss,%mem --sort=-rss | head -n 11
systemd-cgtop --order=memory -b -n 1
O primeiro comando mostra os maiores processos individuais, o segundo agrupa por serviços. Na prática aparecem ali quase sempre os mesmos três suspeitos: o MariaDB com um innodb_buffer_pool_size demasiado alto, o PHP-FPM com um pm.max_children exagerado e uma JVM com um -Xmx demasiado generoso.
Como ponto de partida para o tamanho do ficheiro de swap, esta tabela chega. Num servidor, maior não é melhor, porque um swap que chega mesmo a ser usado por completo torna a máquina impossível de operar.
| Memória RAM | Ficheiro de swap razoável |
|---|---|
| 1 GB | 1 a 2 GB |
| 2 GB | 2 GB |
| 4 a 8 GB | 2 a 4 GB |
| 16 GB ou mais | 4 GB, raramente mais |
Criar o ficheiro de swap
Primeiro convém ver se já existe swap. Os servidores Ubuntu instalados a partir da ISO trazem muitas vezes já um /swap.img, o Debian do instalador traz normalmente uma partição de swap a sério. As imagens cloud de ambas as distribuições costumam não trazer rigorosamente nada.
swapon --show
Se a saída ficar vazia, não há swap nenhum. A seguir verifique o sistema de ficheiros, porque é dele que depende o procedimento:
findmnt -no FSTYPE -T /
Com ext4 ou xfs pode seguir logo em frente. Crie o ficheiro com dd, não com fallocate. O fallocate é mais rápido, mas, consoante o sistema de ficheiros e o kernel, produz um ficheiro com zonas não escritas, e o swapon rejeita-o. O dd escreve zeros a sério e funciona em todo o lado:
dd if=/dev/zero of=/swapfile bs=1M count=2048 status=progress
chmod 600 /swapfile
Antes do passo seguinte compensa uma verificação que muita gente salta. Se debaixo de /swapfile já estiver um swap ligado, por exemplo de uma tentativa anterior ou da configuração de origem da imagem, o mkswap recusa o trabalho com mkswap: error: /swapfile is mounted; will not make swapspace. O que conta aqui é apenas se o caminho consta como ativo em /proc/swaps. Portanto, veja primeiro e, se for o caso, desligue:
swapon --show
swapoff /swapfile
Se em swapon --show não aparecer nenhuma linha com /swapfile, pode saltar o swapoff. Depois disso, escreva a área de swap:
mkswap /swapfile
O mkswap responde com Setting up swapspace version 1, size = 2 GiB (2147479552 bytes) e um UUID novo. Só a seguir é que se liga:
swapon /swapfile
A ordem não é negociável. chmod antes de mkswap, mkswap antes de swapon, e um eventual swapoff antes de tudo o resto.
Como reconhecer que resultou mesmo
O facto de o swapon terminar sem mensagem de erro ainda não prova nada. A prova são estas duas saídas:
swapon --show
free -h
O swapon --show tem de produzir uma linha com /swapfile, file, o tamanho e uma prioridade. No free -h, a linha Swap: tem de ter saltado de 0B para o novo tamanho. Se uma das duas saídas ficar na mesma, o swap não está ativo, independentemente do que o comando tenha dito antes.
Tornar permanente sem pôr o arranque em risco
Um swapon não sobrevive a um reinício. A entrada pertence ao /etc/fstab, e é precisamente ali que se destroem servidores. Primeiro, uma cópia de segurança:
cp /etc/fstab /etc/fstab.bak
echo '/swapfile none swap sw 0 0' >> /etc/fstab
Repare nos dois sinais de maior. Um único > substitui o ficheiro inteiro, e a partir daí a máquina deixa de arrancar como deve ser. A seguir verifique a sintaxe antes de reiniciar:
findmnt --verify
Um aviso é aqui perfeitamente normal e não é motivo para voltar a retirar a linha: [W] non-bind mount source /swapfile is a directory or regular file. Com um ficheiro de swap isso é inevitável, e mesmo assim o comando termina com valor de retorno 0. Se, além disso, faltar ao ficheiro um cabeçalho de swap válido, junta-se-lhe [W] cannot detect on-disk filesystem type, e aí esqueceu-se mesmo de um mkswap.
Se já tiver ligado o swap manualmente mais acima com swapon /swapfile, ele já está ativo. Caso contrário, o swapon -a ativa todas as entradas do fstab sem ser preciso reiniciar. O verdadeiro teste, porém, é outro. O systemd gera uma unit própria a partir de cada linha do fstab; para /swapfile chama-se swapfile.swap. Se essa unit aparecer e estiver ativa, o swap é garantidamente montado no arranque seguinte:
systemctl daemon-reload
systemctl list-units --type swap
O que se espera é uma linha swapfile.swap loaded active active Swap. Se ela faltar, a linha do fstab não está correta e um reinício acabaria sem swap. Só quando isto estiver certo é que compensa reiniciar, com a verificação subsequente através de swapon --show.
Definir a swappiness corretamente
O parâmetro de kernel vm.swappiness controla com que facilidade as páginas anónimas vão para o swap em vez de se descartar cache de ficheiros. O valor por omissão é o mesmo em Debian 12, Debian 13, Ubuntu 22.04 e Ubuntu 24.04, ou seja, 60:
cat /proc/sys/vm/swappiness
Desde o kernel 5.8 o intervalo de valores vai de 0 a 200, e as quatro distribuições estão acima dessa versão. Dois equívocos frequentes: vm.swappiness=0 não desliga o swap, apenas evita a paginação preventiva e deixa o kernel recorrer ao swap na mesma antes de matar seja o que for. E um valor baixo não torna um sistema mais rápido quando o que falta é mesmo memória.
Valores razoáveis: 10 a 20 em servidores de base de dados, 60 em servidores web mistos, 100 ou mais se usar zram. De forma permanente, isso pertence a um ficheiro próprio em /etc/sysctl.d/, não ao /etc/sysctl.conf, que fica no caminho das atualizações de pacotes:
echo 'vm.swappiness = 10' > /etc/sysctl.d/99-swappiness.conf
sysctl --system
cat /proc/sys/vm/swappiness
O terceiro comando é o controlo do resultado. O sysctl --system lê todos os diretórios por uma ordem fixa, e um ficheiro já existente com um número mais alto pode sobrepor-se ao seu valor.
Quando corre mal: as mensagens de erro na íntegra
swapon: /swapfile: insecure permissions 0644, 0600 suggested. É apenas um aviso, o swap funciona à mesma. Ainda assim, corrija, caso contrário qualquer utilizador pode ler o conteúdo dos processos que foram para o swap: chmod 600 /swapfile.
swapon: /swapfile: swapon failed: Invalid argument O erro mais frequente. Ou se esqueceu o mkswap, ou o ficheiro tem buracos. No log do kernel aparece então, adicionalmente, swapon: swapfile has holes. Solução: apagar o ficheiro e voltar a criá-lo com dd em vez de fallocate.
Em btrfs aplica-se o mesmo erro, acrescido de BTRFS warning: swapfile must not be copy-on-write. Aqui a ordem é outra: o ficheiro tem de ser criado vazio e marcado como não copy-on-write antes de ser preenchido. Uma advertência logo à partida, que pode decidir o destino de um servidor em produção: o truncate -s 0 e o rm também são executados quando debaixo desse caminho ainda está montado um swap ativo. O kernel passa depois a apontar para blocos que já não existem. Por isso, se o swapon --show apresentar o caminho como ativo, é obrigatório um swapoff /swapfile antes.
truncate -s 0 /swapfile
chattr +C /swapfile
Depois disso, preencher com dd como de costume, chmod 600, mkswap, swapon. Em subvolumes comprimidos e em snapshots o swap continua a não funcionar.
swapon: /swapfile: swapon failed: Operation not permitted Está dentro de um container. LXC, OpenVZ e Docker partilham o kernel do anfitrião e não podem ligar um swap próprio. Verifique com:
systemd-detect-virt
Se o comando indicar kvm, qemu ou none, está a correr um kernel próprio e o swap é possível. Se indicar lxc, openvz ou docker, só ajuda mais memória ou um produto com virtualização completa. Os servidores root KVM e os servidores dedicados da KernelHost trazem um kernel próprio, e ali o swap pode ser configurado sem restrições.
dd: error writing '/swapfile': No space left on device O disco está demasiado cheio. Primeiro df -h, depois remova o ficheiro escrito pela metade com rm /swapfile, caso contrário continua a ocupar espaço. Também aqui vale a mesma regra: se o swapon --show mostrar o caminho como ativo, um swapoff /swapfile tem de vir antes da eliminação.
O sistema deixa de arrancar e aparece a consola de emergência. É quase sempre uma gralha no /etc/fstab. Inicie sessão através da consola na área de cliente, depois mount -o remount,rw /, retire a linha errada ou volte a copiar o /etc/fstab.bak, e reinicie. Foi exatamente para isto que a cópia de segurança foi criada antes.
Diferenças entre Debian 13, Debian 12, Ubuntu 24.04 e 22.04
Os comandos são idênticos nos quatro sistemas, o estado inicial não.
- Swap já existente: o Ubuntu Server instalado a partir da ISO cria muitas vezes um
/swap.img, o Debian do instalador cria uma partição de swap. As imagens cloud de ambas as distribuições vêm sem swap. Comece sempre porswapon --show. - systemd-oomd: no Ubuntu vem instalado e ativo de origem desde a 22.04, nas duas versões do Debian não faz parte da instalação padrão. Isso explica porque é que software idêntico pode morrer de maneiras diferentes em dois sistemas aparentemente iguais.
- Base de dados: o Debian 12 e o Debian 13 não fornecem
mysql-server, ali corre sempre MariaDB (10.11 no Debian 12, 11.8 no Debian 13). O Ubuntu 22.04 e o 24.04 têm ambos. Os valores por omissão deinnodb_buffer_pool_sizediferem em conformidade, e é precisamente esse valor a causa de OOM mais frequente em servidores pequenos. - Java: o Debian 12 só conhece o OpenJDK 17, o Debian 13 só o OpenJDK 21, o Ubuntu 22.04 e o 24.04 cobrem do 8 ao 21. Uma JVM sem
-Xmxdefinido fica por omissão com um quarto da memória, e com várias instâncias o OOM-Kill está garantido à partida. - Mensagens do kernel: o formato e o texto da mensagem de OOM são iguais nos quatro sistemas, os exemplos acima servem em todo o lado.
- swappiness: 60 como valor por omissão em todos.
Quando o swap ajuda e quando apenas adia o problema
O swap ajuda de forma fiável em picos curtos, por exemplo durante um backup, uma atualização de pacotes ou uma importação noturna. Ajuda em serviços que ocupam muita memória e depois passam dias sem fazer nada, porque essas páginas podem perfeitamente ficar no disco. E, em caso de emergência, dá-lhe minutos durante os quais a sessão SSH ainda responde e é possível intervir, em vez de ficar diante de um servidor morto.
O swap não ajuda quando a necessidade permanente está simplesmente acima da memória instalada. Aí o sistema entra em thrashing: move páginas para o swap e de volta sem parar, a load sobe para valores de dois dígitos, a utilização do CPU mantém-se baixa e tudo espera por I/O. Na prática, isso é pior do que um OOM-Kill limpo, porque nem sequer o início de sessão consegue passar. Dois comandos mostram se está nesse estado:
vmstat 1 5
test -e /proc/pressure/memory && cat /proc/pressure/memory
No vmstat contam as colunas si e so. Valores de três dígitos de forma continuada significam movimento ativo de páginas para dentro e para fora do swap. Em /proc/pressure/memory o que decide é o full avg10: valores acima de 10 significam que o sistema inteiro passa dez por cento do tempo à espera de memória. Acima de 40, a máquina está praticamente morta. O ficheiro, porém, só existe se a Pressure Stall Information estiver ativa no kernel. Os kernels padrão do Debian e do Ubuntu trazem-na, outros kernels não necessariamente. Daí a salvaguarda com test -e: se o ficheiro faltar, a saída fica vazia, em vez de tomar um No such file or directory por uma avaria. A PSI pode ser acrescentada através do parâmetro de arranque do kernel psi=1.
Que processos estão realmente no swap, responde-o esta linha:
grep VmSwap /proc/*/status | sort -k2 -rn | head
O swap também não serve de nada contra um limite de cgroup (CONSTRAINT_MEMCG), contra memória fixada com mlock e contra uma JVM cujo heap esteja configurado acima da RAM existente. O garbage collector percorre regularmente todo o heap e traz de imediato de volta cada página que tinha ido para o swap.
As três ferramentas para quando o swap não chega
O zram cria uma área de swap comprimida dentro da própria RAM. É várias ordens de grandeza mais rápido do que um ficheiro no disco e, consoante os dados, proporciona 20 a 40 por cento de memória efetiva adicional:
apt update
apt install -y zram-tools
A configuração faz-se em /etc/default/zramswap através de ALGO=zstd e PERCENT=50, seguida de systemctl restart zramswap. A verificação faz-se com zramctl e novamente com swapon --show, onde passa a aparecer /dev/zram0. Com zram, o vm.swappiness deve subir para 100 a 180, porque aqui enviar páginas para o swap é barato.
O earlyoom intervém antes de o kernel congelar o sistema e termina processos de forma dirigida, em vez de por heurística:
apt install -y earlyoom
Em /etc/default/earlyoom define, através de EARLYOOM_ARGS, os limiares e protege os processos importantes, por exemplo com -m 5 -s 5 --avoid '(^|/)(sshd|systemd)$'. Assim o acesso por SSH continua disponível enquanto o processo que devora a memória é abatido.
Limites fixos por serviço são a solução mais limpa quando um determinado serviço se descontrola com regularidade. Através de systemctl edit mariadb introduz MemoryHigh=1200M e MemoryMax=1500M. O serviço passa então a ser travado e, se for preciso, terminado sozinho, em vez de arrastar o servidor inteiro consigo. Verificação através de systemctl show mariadb -p MemoryMax.
A correção verdadeira continua a ser, na maioria dos casos, a configuração da aplicação: innodb_buffer_pool_size num valor realista, pm.max_children em função da memória realmente necessária por worker, um -Xmx definido para cada JVM. O swap é a rede de segurança, não a solução.
Reverter, caso não sirva
O caminho de volta é curto e deve seguir esta ordem:
swapoff /swapfile
Com o swap muito ocupado, o comando pode demorar vários minutos, porque todas as páginas têm de voltar para a RAM. Se der em swapoff: /swapfile: swapoff failed: Cannot allocate memory, é porque não há espaço na RAM para o transporte de volta e há que parar serviços primeiro. Só depois de um swapoff bem-sucedido é que se retira a linha do fstab e se apaga o ficheiro:
rm /swapfile
swapon --show
Apagar o ficheiro enquanto ele ainda está montado leva a um sistema cuja gestão de memória aponta para um inode que já não existe. Isso acaba mal. No fim, mais um free -h e um reinício como contraprova.
Perguntas frequentes
De quanto swap preciso num servidor?
Porque é que o journalctl -k não mostra nenhum OOM-Kill, apesar de ter havido um?
Como reconheço se foi o kernel ou um limite do systemd a terminar o processo?
Porque é que o swapon falha com "Invalid argument"?
Posso configurar swap dentro de um container?
vm.swappiness=0 significa que deixa de haver qualquer paginação para o swap?
2026 KernelHost GmbH. Todos os direitos reservados. Este guia está protegido por direitos de autor. A sua republicação noutros sites, na íntegra, em parte ou de forma editada, não é permitida sem o nosso consentimento por escrito. Citações com indicação da fonte e ligação são expressamente bem-vindas.

