
# PM2 einen Server-Neustart überleben lassen (beide Hälften)

Der Server startete neu und die Website kam nicht zurück. Sie kam eine Stunde später zurück, als jemand ein Deployment auslöste. Das sieht nach einem Deployment-Problem aus und ist keines: dieses Deployment hat schlicht wieder von Hand `pm2 start` ausgeführt.

Damit Ihre Anwendung von selbst startet, braucht es zwei getrennte Dinge, und jedes ist ohne das andere wertlos.

## Die zwei Hälften

**`pm2 save`** schreibt die aktuelle Prozessliste nach `~/.pm2/dump.pm2`. PM2 startet wieder, was in dieser Datei steht, und sonst nichts. Ein Deploy-Skript, das `pm2 delete` und `pm2 start` ausführt, aber nie speichert, hinterlässt einen veralteten Dump — oder einen leeren, falls er nie geschrieben wurde.

**`pm2 startup`** installiert eine systemd-Unit, die beim Booten `pm2 resurrect` ausführt und genau diesen Dump liest.

Das eine ohne das andere ergibt einen Bootvorgang, der nichts startet, oder eine gespeicherte Liste, die niemand liest.

## Die Falle

`pm2 startup` als unprivilegierter Benutzer auszuführen **installiert überhaupt nichts**. Der Befehl erkennt systemd, berechnet den Befehl, der die Unit installieren würde, gibt ihn aus und endet. Die Ausgabe ist lang, grün und schließt mit einer Zeile darüber, dass man den Befehl kopieren kann. Das liest sich wie Erfolg. Passiert ist nichts.

Also: als root ausführen und mit systemd prüfen, nicht mit dem, was PM2 ausgegeben hat.

## Die richtige Reihenfolge

Die Anwendung muss laufen, bevor Sie speichern, sonst speichern Sie eine leere Liste.

```bash
# als <user> — prüfen, dass die Anwendung läuft
pm2 status
```

```bash
# als root — die Unit für diesen Benutzer installieren
env PATH=$PATH:/usr/bin pm2 startup systemd -u <user> --hp /home/<user>
```

Das Präfix `env PATH=$PATH:/usr/bin` ist keine Zierde. Der `PATH` von root enthält normalerweise kein Node, und die Unit merkt sich den Pfad, mit dem sie erzeugt wurde. Ohne das Präfix existiert die Unit, findet beim Booten aber kein `node`.

```bash
# wieder als <user> — den Dump schreiben
pm2 save
```

## Beide Hälften einzeln prüfen

Sie fallen unabhängig voneinander aus, also prüft man sie unabhängig voneinander.

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

Das muss `enabled` ausgeben. Alles andere, ob `disabled` oder `Failed to get unit file state`, heißt, dass die Unit nie installiert wurde, egal was PM2 gemeldet hat.

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

Hier muss `<app>` auftauchen. Existiert die Datei nicht oder gibt grep nichts aus, lief `pm2 save` nie, während die Anwendung online war.

## Ohne Neustart testen

Sie müssen keine Produktivmaschine neu starten, um zu wissen, ob das funktioniert.

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

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

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

Ist `<app>` wieder online, funktionieren beide Hälften. Genau das passiert beim Booten.

**Führen Sie nach `pm2 kill` niemals `pm2 save` aus.** Bei getötetem Daemon ist die Prozessliste leer, und das Speichern schreibt diese Leere in den Dump und macht damit genau das kaputt, was Sie gerade testen wollten. Wenn es reflexhaft doch passiert: Anwendung wieder starten und `pm2 save` erneut ausführen.

## `pm2 save` gehört ins Deploy-Skript

Der Dump veraltet, sobald ein Deployment den Prozess verändert. Wenn Ihr Skript löscht und neu startet, muss es danach speichern:

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

Das `|| true` zählt beim ersten Durchlauf, wenn es nichts zu löschen gibt und `pm2 delete` mit einem Exit-Code ungleich null endet, was unter `set -e` das ganze Skript abbricht.

Ohne diese letzte Zeile funktioniert alles darüber weiter, bis zum ersten Deployment, das die Anwendung umbenennt, die Zahl der Instanzen ändert oder den Einstiegspunkt austauscht. Danach beschreibt der Dump einen Prozess, den es nicht mehr gibt, und der nächste Neustart erweckt genau den wieder.

## Noch etwas zur Boot-Reihenfolge

Die erzeugte Unit trägt `After=network.target`. Sie ist nicht nach Ihrer Datenbank einsortiert. Bei einem Kaltstart kann die Anwendung starten, bevor MariaDB oder PostgreSQL Verbindungen annimmt, bei der ersten Abfrage scheitern und sich beenden.

Meist löst sich das von selbst: PM2 startet den Prozess neu, beim zweiten oder dritten Versuch steht die Datenbank, und die Website kommt hoch. Das Anzeichen ist ein Neustartzähler über null bei einem ansonsten gesunden Prozess, in der Spalte `↺` von `pm2 status`.

Erholt sie sich nicht, ordnen Sie die Unit ausdrücklich ein:

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

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

Nur hinzufügen, wenn Sie das Scheitern gesehen haben. Eine Unit hinter einen langsamen Dienst zu hängen, oder hinter einen auf einem anderen Host, tauscht einen Boot, der es erneut versucht, gegen einen, der hängen bleibt.

[Website prüfen](/de) · [Mehr aus dem Blog](/de/blog) · [Über AgentReady](/de/about)
