Effiziente MongoDB-Verbindungen in Payload auf Vercel

Wie sich MongoDB-Atlas-Verbindungen für Payload CMS auf Vercel mit dem afterOpenConnection-Hook und attachDatabasePool verwalten lassen, damit Serverless-Funktionen Idle-Verbindungen vor dem Suspend freigeben.

Porträt eines jungen Mannes mit kurzer brauner Frisur und blauem Hemd vor einer grünen, bewachsenen Landschaft.

Geschrieben von

Jens Becker

Veröffentlicht am

17. April 2026

Zuletzt aktualisiert am

1. Juli 2026

Tags

Payload CMS MongoDB

Payload CMS auf Vercel nutzt den @payloadcms/db-mongodb-Adapter für MongoDB Atlas. In einer serverlosen Umgebung tritt ein wiederkehrendes Fehlermuster auf: Das Connection-Limit des Clusters wird erschöpft, und Anfragen scheitern mit Verbindungsfehlern unter Last – obwohl der Anwendungscode korrekt ist. Dieser Artikel erklärt warum das passiert und wie sich das mit Payloads afterOpenConnection-Hook und Vercels attachDatabasePool lösen lässt.

Warum serverlose Umgebungen MongoDB-Atlas-Verbindungen erschöpfen

MongoDB Atlas begrenzt die Anzahl gleichzeitiger Verbindungen pro Cluster – das Limit hängt vom Cluster-Typ ab. Ein Flex-Cluster (ehemals Shared) erlaubt maximal 500 gleichzeitige Verbindungen, dedizierte Tiers deutlich mehr. Auf serverlosen Plattformen wie Vercel Functions wird dieses Limit schneller erreicht als auf einem langlebigen Server:

  • Cold Starts – jeder neue Cold Start erzeugt einen neuen Connection Pool. Jede neue Function-Instanz öffnet einen eigenen Pool. Eine warme Instanz verwendet denselben Pool wieder – der Druck entsteht also durch neue Instanzen, nicht durch jeden einzelnen Request.
  • Gleichzeitige Instanzen multiplizieren Pools. Bei Traffic-Spitzen skaliert Vercel horizontal auf viele parallele Function-Instanzen. Jede hält einen eigenen Pool – die Gesamtverbindungszahl beträgt grob Instanzen × Pool-Größe.
  • Idle-Verbindungen bleiben offen. Pools halten Verbindungen für Wiederverwendung offen. Ohne Idle-Timeout zählen Verbindungen inaktiver Instanzen weiter gegen das Limit.
  • Suspended Functions können Verbindungen leaken. Wenn eine serverlose Funktion zwischen Requests suspendiert, werden ihre offenen Verbindungen nicht sauber freigegeben – genau das Problem, das Vercels Fluid Compute löst (siehe unten).

Pool begrenzen: maxPoolSize und maxIdleTimeMS

Der erste Hebel sind die Connection-Pool-Einstellungen des Treibers, übergeben über connectOptions des Adapters. Das Pooling übernimmt der MongoDB Node.js-Treiber – nicht Payload. Diese Optionen passen nur sein Verhalten an:

ts
db: mongooseAdapter({
  url: process.env.MONGODB_URI || '',
  // https://www.mongodb.com/docs/manual/administration/connection-pool-overview/
  connectOptions: {
    // Standard-maxPoolSize ist 100 – weit mehr als eine serverlose Instanz benötigt.
    // Mit 10 bleibt `Instanzen × Pool-Größe` auch bei gleichzeitigen Spitzen
    // unter dem Atlas-Limit.
    maxPoolSize: 10,
    // Verbindungen nach 5s Idle schließen, damit suspendierte Instanzen
    // sie nicht weiter belegen.
    maxIdleTimeMS: 5000,
  },
}),
  • maxPoolSize: 10 – begrenzt Verbindungen pro Instanz. Der Treiber-Standard ist 100 – für serverlose Instanzen überdimensioniert. Mit 10 bleiben selbst 50 gleichzeitige Instanzen bei ~500 Verbindungen – an der Grenze eines Flex-Clusters statt weit darüber. An die reale Parallelität anpassen.
  • maxIdleTimeMS: 5000 – schließt Idle-Verbindungen nach 5 Sekunden. Das gibt Verbindungen von Instanzen frei, die keine aktiven Requests mehr bearbeiten, und reduziert die Steady-State-Verbindungszahl. Der aggressive Wert ist für Serverless bewusst gewählt, wo Instanzen schnell kommen und gehen; auf einem langlebigen Server würde man ihn deutlich höher setzen oder ganz weglassen.

Verbindungen beim Suspend freigeben: afterOpenConnection + attachDatabasePool

Pool-Limits reduzieren, wie viele Verbindungen jede Instanz hält – aber sie lösen nicht das Leaking von Verbindungen beim Suspendieren einer Funktion. Vercels Fluid Compute adressiert das mit attachDatabasePool aus @vercel/functions. Es registriert den Datenbank-Pool im Function-Lifecycle und nutzt waitUntil, um die Instanz kurz am Leben zu halten, bis Idle-Pool-Clients freigegeben wurden – bevor die Funktion suspendiert. Neben MongoDB unterstützt es Postgres, MySQL2, MariaDB, ioredis und Cassandra.

Das Problem mit Payload: attachDatabasePool braucht den zugrunde liegenden MongoClient – und bis vor Kurzem bot der MongoDB-Adapter keinen unterstützten Weg, ihn zu erreichen. Genau das löst der afterOpenConnection-Hook. Verfügbar in @payloadcms/db-mongodb, läuft er direkt nach dem Verbindungsaufbau und übergibt den Adapter, aus dem der Client ausgelesen werden kann:

ts
import { mongooseAdapter } from '@payloadcms/db-mongodb'
import { attachDatabasePool } from '@vercel/functions'

// ...

db: mongooseAdapter({
  url: process.env.MONGODB_URI || '',
  connectOptions: {
    maxPoolSize: 10,
    maxIdleTimeMS: 5000,
  },
  // MongoClient an Vercel übergeben, damit Fluid Compute Idle-Verbindungen
  // vor dem Suspend freigibt.
  // https://vercel.com/docs/fluid-compute
  afterOpenConnection: async (adapter) =>
    attachDatabasePool(adapter.connection.getClient()),
}),
  • attachDatabasePool erfordert Fluid Compute. Das Release-on-Suspend-Verhalten setzt waitUntil voraus, das nur auf Vercels Fluid-Compute-Runtime funktioniert. Außerhalb dieser Umgebung ist der Aufruf ein No-op – dieser Schritt ist Vercel-spezifisch.

Wann das nicht reicht – und was sonst hilft

  • Es reduziert Ressourcenverbrauch, nicht die MongoDB-Rechnung. Weniger offene Verbindungen sparen Cluster-RAM und halten die Verbindungszahl unter dem Limit, was die Stabilität verbessert. Sie senken Atlas-Kosten nicht direkt – die Preise richten sich nach Tier, Storage und Compute, nicht nach der Verbindungsanzahl.
  • Es ist an Vercel Fluid Compute gebunden. Auf anderen Hosts oder Runtimes ist eine andere Connection-Management-Strategie nötig; attachDatabasePool hilft dort nicht.
  • Medien aus Objektspeicher ausliefern. Standardmäßig liefert Payload Medien über seine eigene API-Route aus (/api/...). Das Durchsuchen der Admin-Medienbibliothek erzeugt dadurch einen Burst gleichzeitiger HTTP-Anfragen ans CMS – jede trifft eine Function-Instanz und belastet den Connection Pool. Werden Bilder und Dateien direkt von S3 / Cloudflare R2 ausgeliefert, umgeht der Browser Payloads API-Route komplett, und diese Anfragen erreichen weder das CMS noch den Pool. Das kann die Peak-Parallelität spürbar senken, wenn Redakteure aktiv im Admin arbeiten.
  • Überwachen und Lasttests durchführen. Verbindungszahlen via Atlas-Metriken und Vercel-Logs verfolgen und gleichzeitigen Traffic vor Produktiv-Go-live simulieren, damit maxPoolSize zur realen Last passt.
  • SSL alert 80 / transiente TLS-Fehler. Ein konkretes Beispiel für einen transienten Fehler: MongoNetworkError: SSL alert number 80 — Atlas schließt eine bestehende Verbindung bei einem internen Ereignis (z. B. Failover oder Maintenance-Restart), der Treiber setzt daraufhin den gesamten Pool zurück (ResetPool), und alle parallel laufenden Requests scheitern mit MongoPoolClearedError.

Fazit

Payload CMS auf Vercel gegen MongoDB Atlas erfordert kein manuelles Kämpfen gegen Connection-Limits. Pool pro Instanz mit maxPoolSize und maxIdleTimeMS begrenzen, dann den afterOpenConnection-Hook nutzen, um den MongoClient an Vercels attachDatabasePool zu übergeben, damit Fluid Compute Idle-Verbindungen vor dem Suspend freigibt. Konfiguration oben als Ausgangspunkt nehmen, Atlas-Verbindungsmetriken unter Last beobachten und die Pool-Größe an den echten Traffic anpassen.