
# Far sopravvivere PM2 a un riavvio del server

Il server si è riavviato e il sito non è tornato. È tornato un'ora dopo, quando qualcuno ha lanciato un deploy. Sembra un problema di deploy e non lo è: quel deploy si è limitato a eseguire di nuovo `pm2 start` a mano.

Perché la tua applicazione riparta da sola servono due cose distinte, e ognuna senza l'altra non serve a niente.

## Le due metà

**`pm2 save`** scrive l'elenco dei processi attuali in `~/.pm2/dump.pm2`. PM2 rilancia quello che c'è in quel file e nient'altro. Uno script di deploy che fa `pm2 delete` e `pm2 start` ma non salva mai lascia il dump vecchio, o vuoto se non è mai stato scritto.

**`pm2 startup`** installa un'unità systemd che al boot esegue `pm2 resurrect`, ed è lei a leggere quel dump.

Una senza l'altra ti dà un avvio che non tira su niente, oppure un elenco salvato che non legge nessuno.

## La trappola

Eseguire `pm2 startup` con un utente senza privilegi **non installa assolutamente nulla**. Il comando rileva systemd, calcola il comando che installerebbe l'unità, lo stampa e termina. L'output è lungo, verde, e finisce con una riga che ti invita a copiare il comando. Sembra un successo. Non è successo niente.

Quindi: eseguilo come root, e verifica con systemd invece che con quello che ha stampato PM2.

## L'ordine giusto

L'applicazione deve essere in esecuzione prima di salvare, altrimenti salvi un elenco vuoto.

```bash
# come <user> — conferma che l'applicazione gira
pm2 status
```

```bash
# come root — installa l'unità per quell'utente
env PATH=$PATH:/usr/bin pm2 startup systemd -u <user> --hp /home/<user>
```

Il prefisso `env PATH=$PATH:/usr/bin` non è un ornamento. Il `PATH` di root di solito non contiene Node, e l'unità si porta dietro il percorso con cui è stata generata. Senza quel prefisso l'unità esiste, ma al boot non trova `node`.

```bash
# di nuovo come <user> — scrivi il dump
pm2 save
```

## Verifica ogni metà separatamente

Si rompono in modo indipendente, quindi si controllano in modo indipendente.

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

Deve rispondere `enabled`. Qualsiasi altra cosa, `disabled` o `Failed to get unit file state`, vuol dire che l'unità non è mai stata installata, checché ne abbia detto PM2.

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

Lì deve comparire `<app>`. Se il file non esiste, o il grep non stampa niente, `pm2 save` non è mai stato eseguito con l'applicazione online.

## Provalo senza riavviare

Non serve riavviare una macchina in produzione per sapere se funziona.

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

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

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

Se `<app>` è tornata online, entrambe le metà funzionano. È esattamente quello che fa il boot.

**Non eseguire mai `pm2 save` dopo `pm2 kill`.** Con il demone ucciso l'elenco dei processi è vuoto, e salvando scrivi quel vuoto nel dump, disfacendo proprio la cosa che stavi provando. Se ti scappa per riflesso, riavvia l'applicazione e rifai `pm2 save`.

## Metti `pm2 save` nello script di deploy

Il dump si disallinea appena un deploy tocca il processo. Se il tuo script cancella e riavvia, deve salvare subito dopo:

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

Il `|| true` conta alla prima esecuzione, quando non c'è niente da cancellare e `pm2 delete` esce con codice diverso da zero, cosa che sotto `set -e` ammazza tutto lo script.

Senza quell'ultima riga tutto quanto sopra continua a funzionare fino al primo deploy che rinomina l'applicazione, cambia il numero di istanze o sposta il punto di ingresso. Da lì in poi il dump descrive un processo che non esiste più, e il riavvio successivo resuscita quello.

## Un'ultima cosa sull'ordine di avvio

L'unità generata porta `After=network.target`. Non è ordinata dopo il tuo database. A freddo l'applicazione può partire prima che MariaDB o PostgreSQL accettino connessioni, fallire alla prima query e uscire.

Di solito si risolve da sé: PM2 riavvia il processo, al secondo o terzo tentativo il database è pronto e il sito sale. L'indizio è un contatore di riavvii sopra lo zero su un processo per il resto sano, nella colonna `↺` di `pm2 status`.

Se invece non si riprende, allora ordina l'unità in modo esplicito:

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

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

Aggiungilo solo se l'hai visto fallire. Mettere un'unità dietro a un servizio lento, o che vive su un'altra macchina, ti scambia un avvio che riprova con uno che resta appeso.

[Analizza il tuo sito](/it) · [Altri articoli](/it/blog) · [Chi siamo](/it/about)
