
# Fazer o PM2 sobreviver a um reinício do servidor

O servidor reiniciou e o site não voltou. Voltou uma hora depois, quando alguém lançou um deploy. Parece um problema de deploy e não é: esse deploy limitou-se a correr `pm2 start` outra vez à mão.

Para que a aplicação arranque sozinha são precisas duas coisas distintas, e cada uma sem a outra não serve de nada.

## As duas metades

**`pm2 save`** escreve a lista de processos atual em `~/.pm2/dump.pm2`. O PM2 relança o que estiver nesse ficheiro e mais nada. Um script de deploy que faz `pm2 delete` e `pm2 start` mas nunca guarda deixa o dump desatualizado, ou vazio se nunca chegou a ser escrito.

**`pm2 startup`** instala uma unidade systemd que corre `pm2 resurrect` no arranque, e é ela que lê esse dump.

Uma sem a outra dá-te um arranque que não levanta nada, ou uma lista guardada que ninguém lê.

## A armadilha

Correr `pm2 startup` com um utilizador sem privilégios **não instala absolutamente nada**. O comando deteta o systemd, calcula o comando que instalaria a unidade, imprime-o e termina. A saída é longa, aparece a verde e acaba com uma linha a dizer que podes copiar o comando. Parece sucesso. Não aconteceu nada.

Portanto: corre-o como root e confirma com o systemd, não com o que o PM2 imprimiu.

## A ordem correta

A aplicação tem de estar a correr antes de guardares, senão guardas uma lista vazia.

```bash
# como <user> — confirma que a aplicação está a correr
pm2 status
```

```bash
# como root — instala a unidade para esse utilizador
env PATH=$PATH:/usr/bin pm2 startup systemd -u <user> --hp /home/<user>
```

O prefixo `env PATH=$PATH:/usr/bin` não é enfeite. O `PATH` do root normalmente não tem Node, e a unidade guarda o caminho com que foi gerada. Sem esse prefixo a unidade existe, mas no arranque não encontra o `node`.

```bash
# outra vez como <user> — escreve o dump
pm2 save
```

## Verifica cada metade em separado

Falham em separado, por isso verificam-se em separado.

```bash
systemctl is-enabled pm2-<user>
```

Tem de responder `enabled`. Qualquer outra coisa, seja `disabled` ou `Failed to get unit file state`, quer dizer que a unidade nunca foi instalada, diga o que disser o PM2.

```bash
grep -o '"name":"[^"]*"' /home/<user>/.pm2/dump.pm2
```

Aí tem de aparecer `<app>`. Se o ficheiro não existe, ou o grep não imprime nada, o `pm2 save` nunca correu com a aplicação em linha.

## Testa sem reiniciar

Não é preciso reiniciar uma máquina em produção para saber se isto funciona.

```bash
# como <user>
pm2 kill
```

```bash
# como root
systemctl start pm2-<user>
```

```bash
# como <user>
pm2 status
curl -sf http://127.0.0.1:3000/health
```

Se `<app>` voltou e está em linha, as duas metades funcionam. É exatamente o que o arranque faz.

**Nunca corras `pm2 save` depois de `pm2 kill`.** Com o daemon morto a lista de processos está vazia, e ao guardar escreves esse vazio no dump, desfazendo precisamente aquilo que estavas a testar. Se o fizeres por reflexo, levanta a aplicação outra vez e repete o `pm2 save`.

## Põe o `pm2 save` no script de deploy

O dump desatualiza-se assim que um deploy mexe no processo. Se o teu script apaga e volta a arrancar, tem de guardar a seguir:

```bash
pm2 delete <app> || true
pm2 start ecosystem.config.cjs --only <app>
pm2 save
```

O `|| true` conta na primeira execução, quando não há nada para apagar e o `pm2 delete` sai com código diferente de zero, o que mata o script inteiro com `set -e`.

Sem essa última linha tudo o que está acima continua a funcionar até ao primeiro deploy que mude o nome da aplicação, altere o número de instâncias ou troque o ponto de entrada. A partir daí o dump descreve um processo que já não existe, e o reinício seguinte ressuscita esse.

## Mais uma coisa sobre a ordem de arranque

A unidade gerada leva `After=network.target`. Não está ordenada depois da tua base de dados. Num arranque a frio a aplicação pode subir antes de o MariaDB ou o PostgreSQL aceitarem ligações, falhar na primeira consulta e sair.

Normalmente resolve-se sozinho: o PM2 reinicia o processo, à segunda ou terceira tentativa a base de dados já está pronta, e o site sobe. A pista é um contador de reinícios acima de zero num processo que de resto está saudável, na coluna `↺` do `pm2 status`.

Se não recuperar, então ordena a unidade de forma explícita:

```bash
sudo systemctl edit pm2-<user>
```

```ini
[Unit]
After=network.target mariadb.service
```

Acrescenta isso só se já o viste falhar. Pôr uma unidade atrás de um serviço lento, ou que vive noutra máquina, troca um arranque que tenta outra vez por um que fica pendurado.

[Analisa o teu site](/pt) · [Mais artigos](/pt/blog) · [Sobre o AgentReady](/pt/about)
