
05. Oktober 2026 · 10 Min.
Node 20 ist vorbei: Runtime-Upgrade ohne Blindflug
Vercel blockiert seit dem 1. Oktober neue Node-20-Deployments, GitHub Actions läuft bereits auf Node 24. Ein praktischer Plan für Updates, die nicht erst in Produktion getestet werden.




Der alte Stand läuft — bis zum nächsten Deploy
Seit dem 1. Oktober 2026 lässt sich Node.js 20 bei Vercel nicht mehr als Runtime für neue Builds und Functions auswählen. Bestehende Deployments laufen weiter. Genau das macht die Situation tückisch: Die Website ist grün, Monitoring bleibt ruhig, und erst ein dringender Hotfix scheitert beim Deployment.
Parallel ist auch die CI weitergezogen. GitHub Actions führt JavaScript Actions seit dem 23. September mit Node 24 aus; die frühere Ausweichvariable für Node 20 ist nicht mehr verfügbar. Wer eigene Actions pflegt, muss runs.using auf node24 umstellen. Wer Actions nur verwendet, muss Versionen einsetzen, die Node 24 unterstützen.
Node 20 selbst ist End of Life. Das Node.js-Projekt liefert dafür keine regulären Updates und Sicherheitspatches mehr. Die Frage lautet deshalb nicht mehr, ob ein Upgrade sinnvoll wäre. Sie lautet: Wie modernisieren wir Runtime, Build und CI gemeinsam, ohne einen Freitagabend in eine Fehlersuche zu verwandeln?
Eine Anwendung hat selten nur eine Node-Version
In vielen Repositories stehen mehrere Wahrheiten nebeneinander:
- package.json nennt eine Version im Feld engines;
- .nvmrc oder .node-version steuert lokale Umgebungen;
- ein Dockerfile verwendet sein eigenes Basis-Image;
- CI richtet Node separat ein;
- die Hosting-Plattform besitzt eine Projekteinstellung;
- Entwickler haben noch eine andere Version global installiert;
- JavaScript Actions bringen eine eigene eingebettete Runtime mit.
Solange alle ungefähr zusammenpassen, fällt das kaum auf. Beim Major-Upgrade entsteht dann ein absurdes Bild: lokal wird mit Node 24 getestet, das Image baut auf Node 20, eine Pipeline nutzt Node 22 und die Plattform entscheidet anhand einer alten Einstellung.
Der erste Schritt ist daher kein Paketupdate, sondern eine Inventur. Sucht im gesamten Repository nach node, engines, setup-node, FROM node:, .nvmrc, .node-version und plattformspezifischen Konfigurationen. Notiert für jede Stelle, ob sie Entwicklung, Test, Build oder Produktion steuert.
Das Ergebnis sollte eine einzige beabsichtigte Runtime ergeben. Für Vercel empfiehlt die aktuelle Migrationsanleitung Node 24; laut Node.js-Releaseübersicht ist 24 eine LTS-Linie. Node 26 ist Anfang Oktober noch als Current geführt. Für eine reguläre Produktionsmigration ist die von der Plattform empfohlene LTS-Version deshalb der ruhigere Zielpunkt.
Erst reproduzieren, dann reparieren
Ein Upgrade-Test auf dem Laptop beweist wenig, wenn dort alte node_modules, Build-Caches und globale Werkzeuge liegen. Beginnt mit einer frischen Umgebung, die dem späteren Build entspricht.
Ein belastbarer erster Durchlauf enthält:
- frischen Checkout;
- Node 24 aus derselben Quelle wie in CI;
- Installation über den bestehenden Lockfile-Befehl, etwa npm ci;
- vollständigen Build;
- Unit- und Integrationstests;
- Start der tatsächlich deployten Entry Points;
- einen kleinen Smoke-Test gegen die laufende Anwendung.
Schlägt schon die Installation fehl, ist das ein gutes Ergebnis: Der Fehler ist jetzt reproduzierbar. Häufig liegen die Ursachen nicht im Anwendungscode, sondern in nativen Add-ons, Install-Skripten, veralteten Toolchains oder Paketen, die die neue Runtime noch nicht akzeptieren.
Wichtig ist, nicht sofort den Lockfile zu löschen und sämtliche Dependencies gleichzeitig zu aktualisieren. Dann lassen sich Runtime-Effekt und Paket-Effekt kaum noch trennen. Zuerst den unveränderten Stand unter Node 24 ausführen, Fehler dokumentieren und nur die nachweislich blockierenden Pakete anheben. Ein breiter Dependency-Refresh kann danach als eigener Schritt folgen.
Zwei Spuren für einen verständlichen Vergleich
Solange die alte Anwendung noch baubar ist, hilft eine temporäre CI-Matrix mit Node 20 und Node 24. Beide Spuren führen dieselben Befehle aus. Das schafft keinen dauerhaften Support für Node 20; es zeigt lediglich, was sich durch die Runtime ändert.
Vergleicht nicht nur rot und grün. Sammelt zusätzlich:
- Installationszeit und Warnungen;
- Testanzahl und übersprungene Tests;
- Größe und Inhalt des Build-Artefakts;
- Startzeit und Speicherverbrauch im Smoke-Test;
- Warnungen zu veralteten oder entfernten APIs;
- Ergebnisse ausgewählter API- und Browser-Flows.
Die Matrix wird entfernt, sobald Node 24 der bestätigte Standard ist. Sonst konserviert sie die alte Linie und verdoppelt dauerhaft Laufzeit und Wartung.
Den echten Server testen, nicht nur den Compiler
Ein erfolgreicher Frontend-Build ist kein vollständiger Runtime-Test. Node steckt oft in unsichtbaren Teilen des Produkts: API Routes, Server Actions, Bildverarbeitung, Webhooks, PDF-Generierung, Queue Worker, Cronjobs oder Build-Plugins.
Für jede ausführbare Einheit braucht es mindestens einen Start- oder Aufruftest. Bei einer API kann das ein Health-Endpunkt plus eine repräsentative Anfrage sein. Bei einem Worker wird ein reales Event mit Testdaten verarbeitet. Bei einem Bilddienst gehört eine kleine Datei durch denselben Decoder und Encoder geschickt, die später in Produktion laufen.
Besondere Aufmerksamkeit verdienen Grenzen zur Außenwelt:
- TLS-Verbindungen und Zertifikate;
- Datenbanktreiber und native Bibliotheken;
- Datei- und Pfadoperationen auf Linux;
- ESM- und CommonJS-Übergänge;
- Streams, Uploads und große Responses;
- Kindprozesse und CLI-Werkzeuge;
- Zeit-, Locale- und Zeitzonenlogik.
Nicht jede dieser Stellen bricht bei Node 24. Aber genau dort verstecken sich Unterschiede, die ein Typecheck nicht sieht.
CI-Actions sind ein eigener Migrationspfad
GitHub Actions unterscheidet zwischen der Node-Version eures Projekts und der Runtime einer JavaScript Action. actions/setup-node kann euer Projekt auf Node 24 setzen, während eine alte Drittanbieter-Action intern noch für Node 20 gebaut wurde.
Für Workflow-Nutzer bedeutet das:
- jede verwendete Action inventarisieren;
- auf eine aktuelle, Node-24-fähige Major- oder Commit-Version wechseln;
- Release Notes auf geänderte Inputs und Outputs prüfen;
- Workflows aus Pull Request, Push, Tag und Schedule testen;
- veraltete Ausnahmen und Runtime-Opt-outs entfernen.
Für Maintainer eigener Actions kommt mehr dazu: runs.using: node24, ein neu gebautes Distributionsbundle und ein veröffentlichter Release-Tag. GitHub weist außerdem darauf hin, dass Node 24 ältere Self-hosted-Umgebungen einschränkt: macOS 13.4 und älter sowie ARM32 werden nicht offiziell unterstützt. Die Runner-Inventur gehört deshalb in denselben Change.
Die Version an einer Stelle entscheiden
Eine gute Konfiguration macht Abweichungen sichtbar. Das Feld engines in package.json ist ein sinnvoller Vertrag für Hosting und Paketwerkzeuge. Eine .nvmrc oder .node-version sorgt für denselben lokalen Einstieg. CI liest idealerweise diese Quelle oder prüft explizit, dass beide Werte übereinstimmen.
Ein minimales Zielbild:
- package.json: Node 24 als Produktionsvertrag;
- lokale Versionsdatei: derselbe Major;
- CI: dieselbe Version für Installation, Test und Build;
- Docker: passendes, bewusst gepinntes Node-24-Image;
- Hosting: keine widersprechende manuelle Einstellung;
- Dokumentation: ein Befehl zum Aktivieren und ein Befehl zum Prüfen.
Temporär kann ein Health- oder Diagnose-Endpunkt process.version ausgeben, damit das Team die laufende Runtime verifiziert. Diese Information gehört nicht zwingend dauerhaft öffentlich ins Produkt. Für den Rollout reicht ein geschützter interner Check oder ein klarer Start-Logeintrag.
Caches nicht zum Beweismittel machen
Ein Cache beschleunigt bekannte Eingaben. Er sollte keine alte Runtime in einen neuen Build schmuggeln. Der Cache-Key muss daher mindestens Betriebssystem, Architektur, Node-Major und Lockfile berücksichtigen.
Beim ersten Node-24-Durchlauf würden wir Abhängigkeits- und Build-Caches bewusst leer starten. Danach lässt sich ein neuer Cache füllen. Das kostet Minuten, spart aber die deutlich teurere Frage, ob ein grüner Build nur deshalb grün war, weil vorkompilierte Artefakte aus Node 20 wiederverwendet wurden.
Dasselbe gilt für Docker-Layer, Turborepo- oder Next.js-Caches und native Binärpakete. Eine Runtime-Migration ist ein Cache-Boundary.
Das Deployment braucht einen Rückweg
Der sichere Rollout beginnt nicht mit „Deploy“ sondern mit einer Antwort auf: Was tun wir, wenn Fehler erst unter realer Last auftreten?
Vor dem Wechsel halten wir fest:
- welches letzte Deployment sicher funktioniert;
- wie es ohne Neubuild wieder aktiviert wird;
- welche Datenbankänderungen unabhängig zurückrollbar sind;
- welche Metriken und Fehlerraten wir beobachten;
- wer die Entscheidung zum Abbruch trifft.
Wo die Plattform es ermöglicht, geht Node 24 zuerst in Preview oder Staging, dann auf einen kleinen Teil des Traffics und erst danach vollständig live. Ein Rollback auf ein bestehendes altes Artefakt kann kurzfristig helfen. Er löst aber nicht das Runtime-Problem, denn ein neues Node-20-Deployment bleibt blockiert. Das Zeitfenster dient der Reparatur, nicht dem Aufschub.
Der Container ist eine Brücke, kein Upgrade
Vercel beschreibt für Teams, die den Termin nicht schaffen, einen Container als Ausweichweg: Ein eigenes Image kann Node 20 weiterhin mitbringen, weil die Runtime-Einstellung der Plattform dort nicht greift.
Technisch kann das einen blockierten Deploy wieder ermöglichen. Organisatorisch übernimmt das Team damit jedoch die Verantwortung für Basis-Image, Betriebssystempakete und Runtime-Sicherheit. Das Node.js-Projekt warnt bei EOL-Linien ausdrücklich vor ausbleibenden Sicherheitspatches, Toolchain-Brüchen und zunehmender Inkompatibilität im Ökosystem.
Darum braucht diese Brücke ein Ablaufdatum, einen Owner und ein Upgrade-Ticket. „Läuft im Container“ ist kein Lebenszyklusmodell.
Ein Upgrade-PR, den man prüfen kann
Ein überschaubarer Pull Request enthält idealerweise:
- alle Runtime-Pins auf Node 24;
- nur die notwendigen Dependency-Anpassungen;
- aktualisierte JavaScript Actions;
- neue oder ergänzte Smoke-Tests;
- Cache-Keys mit Node-Major;
- kurze Migrations- und Rollback-Notizen;
- Belege aus frischem Build, Tests und Preview.
Was nicht hinein gehört: eine Framework-Migration, ein neues Bundling-System, großflächiges Refactoring und hundert unverbundene Paketupdates. Je kleiner die Änderung, desto klarer lässt sich ein Fehler auf Runtime, Dependency oder Deployment zurückführen.
Runtime-Pflege ist Teil des Produkts
Node-Versionen sind keine unsichtbare Infrastruktur. Sie bestimmen, ob ein Hotfix gebaut werden kann, ob Abhängigkeiten Patches erhalten und ob die CI dasselbe ausführt wie Produktion.
Die aktuelle Node-20-Abschaltung zeigt ein typisches Muster: Das alte System wirkt stabil, bis der nächste notwendige Change die veraltete Grundlage berührt. Gute Teams warten nicht auf diesen Moment. Sie führen eine Runtime-Inventur, bauen frisch, testen die echten Entry Points und planen den Rückweg.
Das Ziel ist nicht nur ein grünes Deployment mit Node 24. Das Ziel ist ein wiederholbarer Upgrade-Prozess, bei dem die nächste Runtime-Version keine Überraschung mehr ist.
Quellen



