Efficient MongoDB Connections in Payload on Vercel

This article shows how to manage MongoDB Atlas connections for Payload CMS on Vercel using the afterOpenConnection hook together with Vercel's attachDatabasePool, so serverless functions release idle connections before they suspend.

Portrait of a young man with short brown hair and blue shirt in front of a green, lush landscape.

Written by

Jens Becker

Published at

April 17, 2026

Last updated on

July 1, 2026

Tags

Payload CMS MongoDB

Payload CMS deployed to Vercel uses the @payloadcms/db-mongodb adapter to talk to MongoDB Atlas. In a serverless setup, a recurring failure mode is exhausting the cluster's connection limit: requests start failing with connection errors under concurrency, even though the application code is correct. This article covers why that happens and how to manage it with Payload's afterOpenConnection hook and Vercel's attachDatabasePool.

Why Serverless Exhausts MongoDB Atlas Connections

MongoDB Atlas enforces a maximum number of concurrent connections per cluster, and the limit depends on the cluster type. A Flex (formerly shared) cluster caps concurrent connections at 500, while dedicated tiers allow far more. On serverless platforms like Vercel Functions, that ceiling is reached more easily than on a long-lived server, for a few reasons:

  • Cold starts — a new instance creates a new connection pool. Each cold start spins up a fresh function instance, and the MongoDB driver opens a new pool for it. A warm instance reuses its existing pool, so the pressure comes from new instances, not from every request.
  • Concurrent instances multiply pools. Vercel scales out by running many function instances in parallel during traffic spikes. Each instance maintains its own pool, so the total connection count is roughly instances × pool size.
  • Idle connections linger. Pools keep connections open for reuse. Without an idle timeout, connections from instances that are no longer serving traffic stay open and count against the limit.
  • Suspended functions can leak connections. When a serverless function suspends between requests, its open connections are not cleanly returned — the core problem Vercel's Fluid Compute tooling addresses (see below).

Capping the Pool: maxPoolSize and maxIdleTimeMS

The first lever is the driver's own connection pool settings, passed through the adapter's connectOptions. The MongoDB Node.js driver — not Payload — manages the pool; these options only tune its behavior:

ts
db: mongooseAdapter({
  url: process.env.MONGODB_URI || '',
  // https://www.mongodb.com/docs/manual/administration/connection-pool-overview/
  connectOptions: {
    // Default maxPoolSize is 100 — far more than a serverless instance needs.
    // Capping at 10 keeps `instances × poolSize` under the Atlas limit during
    // concurrent spikes.
    maxPoolSize: 10,
    // Close connections idle for 5s so suspended/idle instances stop holding them.
    maxIdleTimeMS: 5000,
  },
}),
  • maxPoolSize: 10 — caps connections per instance. The driver default is 100, which is excessive for a serverless instance. With a cap of 10, even 50 concurrent instances stay at ~500 connections — at the edge of a Flex cluster rather than far past it. Tune it to your real concurrency.
  • maxIdleTimeMS: 5000 — closes idle connections after 5 seconds. This frees connections held by instances that are no longer actively serving requests, reducing the steady-state count. The aggressive value is deliberate for serverless, where instances come and go quickly; on a long-lived server you would set it much higher or not at all.

Releasing Connections on Suspend: afterOpenConnection + attachDatabasePool

Pool caps reduce how many connections each instance holds, but they don't solve connections leaking when a function suspends. Vercel's Fluid Compute addresses this with attachDatabasePool from @vercel/functions. It registers the database pool with the function lifecycle and uses waitUntil to keep the instance alive just long enough to release idle pool clients before the function suspends — preventing the connection leak that serverless platforms otherwise can't avoid. It supports MongoDB alongside Postgres, MySQL2, MariaDB, ioredis, and Cassandra.

The catch with Payload: attachDatabasePool needs the underlying MongoClient, and until recently the MongoDB adapter offered no supported way to reach it. That is exactly what the afterOpenConnection hook solves. Available in @payloadcms/db-mongodb (the project this article is based on runs 3.85.0), the hook runs right after the connection is established and hands you the adapter, from which you can get the client:

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

// ...

db: mongooseAdapter({
  url: process.env.MONGODB_URI || '',
  connectOptions: {
    maxPoolSize: 10,
    maxIdleTimeMS: 5000,
  },
  // Hand the live MongoClient to Vercel so Fluid Compute can release idle
  // connections before the function suspends.
  // https://vercel.com/docs/fluid-compute
  afterOpenConnection: async (adapter) =>
    attachDatabasePool(adapter.connection.getClient()),
}),
  • attachDatabasePool requires Fluid Compute. The release-on-suspend behavior depends on waitUntil, which only works on Vercel's Fluid Compute runtime. Outside that environment the call is effectively a no-op, so this step is Vercel-specific.

When This Isn't Enough — and What Else Helps

  • It reduces resource usage, not your MongoDB bill. Fewer open connections save cluster RAM and keep you under connection limits, which improves stability. They don't, on their own, lower Atlas costs — pricing is driven by tier, storage, and compute, not connection count.
  • It's tied to Vercel Fluid Compute. On other hosts or runtimes you need a different connection-management strategy; the attachDatabasePool step won't help there.
  • Serve media from object storage. By default, Payload serves media through its own API route (/api/...), which means browsing the admin media library triggers a burst of concurrent HTTP requests to the CMS — each one hitting a function instance and consuming connections from the pool. Routing images and files directly from S3 / Cloudflare R2 bypasses Payload's API route entirely: the browser fetches assets directly from object storage, so those requests never reach the CMS and never touch the connection pool. This can meaningfully reduce peak concurrency when editors are active in the admin.
  • Monitor and load-test. Track connection counts via Atlas metrics and Vercel logs, and simulate concurrent traffic before production so your maxPoolSize matches real demand.
  • SSL alert 80 / transient TLS drops. A concrete example of a transient failure: MongoNetworkError: SSL alert number 80 — Atlas closes an established connection during an internal event such as a failover or maintenance restart, the driver resets the entire pool (ResetPool), and all in-flight requests fail with MongoPoolClearedError.

Conclusion

Running Payload CMS on Vercel against MongoDB Atlas doesn't require fighting connection limits by hand. Cap each instance's pool with maxPoolSize and maxIdleTimeMS, then use the afterOpenConnection hook to hand the MongoClient to Vercel's attachDatabasePool so Fluid Compute releases idle connections before functions suspend. Start from the configuration above, watch your Atlas connection metrics under load, and tune the pool size to your traffic.