You’ve started a Payload project, and unless your template already sets one, create-payload-app asks you to pick a database. As of November 2026, Payload 3.93.0 officially supports three, MongoDB, Postgres and SQLite, plus adapters for Vercel Postgres and Cloudflare D1. Which one you should use depends on the project and how it’s set up, so there’s no single right choice, and Payload’s database docs cover each adapter in detail.
This guide covers which databases Payload supports, the ones people ask about that it doesn’t, how the three differ inside Payload, how to choose, and what to set up once you’ve picked. It’s written from how we build on Payload, where we’ve used all three databases.
- Which databases does Payload support?
- Can Payload use MySQL, Supabase, Firebase or Redis?
- Postgres, MongoDB or SQLite: what’s the difference in Payload?
- How to choose a database for your Payload project
- What changes after you pick Postgres or SQLite
- Can you add Payload to an existing database, or switch later?
- Payload databases: common questions
- What to do next
Which databases does Payload support?
Payload supports MongoDB, Postgres and SQLite through official database adapters. Each adapter is its own package, installed beside Payload and set in the db property of your Payload config. Two more adapters cover Postgres on Vercel and SQLite on Cloudflare.
| Database | Package | How it connects | Status | Usually hosted on |
|---|---|---|---|---|
| MongoDB | @payloadcms/db-mongodb | Mongoose | official | MongoDB Atlas, or your own server |
| Postgres | @payloadcms/db-postgres | Drizzle ORM and node-postgres | official | Neon, Supabase, or any Postgres server |
| Vercel Postgres | @payloadcms/db-vercel-postgres | Drizzle ORM and @vercel/postgres | official | Neon or another Vercel Marketplace provider |
| SQLite | @payloadcms/db-sqlite | Drizzle ORM and libSQL | official | a local file, or Turso |
| Cloudflare D1 | @payloadcms/db-d1-sqlite | a binding in your Worker | beta | Cloudflare Workers |
Vercel’s own Postgres product is no longer available. Vercel moved its existing databases to Neon in December 2024, and new projects add Postgres from the Vercel Marketplace, from providers such as Neon and Supabase.
For example, a Postgres project sets the adapter like this:
import { buildConfig } from 'payload'
import { postgresAdapter } from '@payloadcms/db-postgres'
export default buildConfig({
// collections, globals and the rest of your config
db: postgresAdapter({
pool: { connectionString: process.env.DATABASE_URL },
}),
})Can Payload use MySQL, Supabase, Firebase or Redis?
Payload works with two of these as they are, one through an unofficial route, and one only for its key-value store.
MySQL
No. Payload has no MySQL adapter, and as of 7 November 2026 there’s no @payloadcms/db-mysql package on npm. Requests for one on Payload’s GitHub go back to May 2022, and the latest, from April 2026, is still an open feature request.
If you’re starting fresh, Postgres is the nearest relational option. If MySQL is fixed because other systems already use the database, Payload isn’t the right tool for that database, and our list of Payload CMS alternatives covers CMSs that run on it.
Supabase and Neon
Yes. Supabase and Neon both run Postgres, so Payload connects to them with @payloadcms/db-postgres and the connection string from their dashboard. A community guide to setting up Payload with Supabase, from June 2024, walks through it step by step. Neon is the database our template uses in production. On Neon, use the pooled connection string for serverless functions and a direct connection for schema migrations.
Firebase, DocumentDB and Cosmos DB
Only unofficially. The MongoDB adapter exports compatibilityOptions for Firebase’s Firestore, Amazon DocumentDB and Azure Cosmos DB, but these databases aren’t officially supported, so you may still hit issues with the settings applied. For example, Cosmos DB can’t run a transaction that updates documents in two or more collections, which Payload’s hooks commonly do.
Redis
Not as Payload’s database. Payload keeps a key-value store, read and written with payload.kv, and by default it lives in a collection in your main database. The @payloadcms/kv-redis adapter, in beta, moves that store to Redis. Your content stays in MongoDB, Postgres or SQLite either way.
Postgres, MongoDB or SQLite: what’s the difference in Payload?
The difference is how your content is stored. MongoDB keeps each Payload document as one database document, while Postgres and SQLite spread it across tables that the adapter creates and keeps in step with your config.
For example, take a site for a two-day conference in Dubai. Each talk has a title and abstract in English and Arabic, a page layout built from blocks, and relationships to its speakers. In MongoDB, a talk is one document with both languages, the blocks and the speakers’ IDs inside it. In Postgres, the same talk becomes a row in a talks table, with its translated fields in talks_locales, its speaker links in talks_rels, each block type in a table of its own, and its drafts and versions in _talks_v.
Nearly every Payload feature works on all three. The one gap is the Point field, which SQLite doesn’t support yet.
| MongoDB | Postgres | SQLite | |
|---|---|---|---|
| How a talk is stored | one document | rows across several tables | rows across several tables, in one file |
| When you change a collection | no schema change | a migration | a migration |
| Relationships | checked by Payload | enforced by the database | enforced by the database |
| Many writes at once | yes | yes | one writer at a time |
| Direct access beside Payload | Mongoose models | Drizzle | Drizzle |
| Features missing | none listed | none listed | the Point field |
MongoDB suits you if you’d rather keep the database simple, don’t want to keep production and staging in sync through schema changes, localise most of your content, or use a lot of arrays, blocks or hasMany select fields. Postgres or SQLite suits you if you’re comfortable with migrations, want the database to enforce consistency, or have many relationships between collections that must be enforced, which matches Payload’s own guidance on choosing a database. If most of your content is localised, our guide to Payload localisation covers what that changes, including how the SQL adapters keep a unique field unique within each locale.
How to choose a database for your Payload project
Five questions about how the project will be used settle most choices:
- Does another system need the same database? If a CRM, a reporting tool or an older app already writes to it, keep that database for them, and connect Payload to it another way, as the section on existing databases shows.
- How often will the content model change after launch? On Postgres and SQLite, each change is a migration your deploy has to run. MongoDB doesn’t need one.
- Is most of the content localised, or built from blocks and arrays? That’s where MongoDB’s one-document shape helps most.
- Will many people write at the same time? Orders, bookings and registrations arriving together suit Postgres or MongoDB, because SQLite allows one writer at a time.
- Where will Payload run? Cloudflare Workers means D1. A serverless host such as Vercel opens many short-lived connections, which a pooled database connection handles, and Neon’s pooler accepts up to 10,000 of them. A long-running server can use any of them, and our guide to where Payload can run covers each kind of host.
For example, the conference site has speakers related to talks, translations on every talk, and hundreds of registrations arriving at once when tickets go on sale. MongoDB would hold the bilingual talks well, but Postgres fits the site better, because the registrations need many writes at once and the organisers will want them reported by talk and speaker, which is a join across relationships the database enforces.
What we use
We’ve built Payload projects on SQLite, MongoDB and Postgres. Our project template starts every new build on Postgres, with a local Postgres database for development and Neon in production. Postgres gives us relationships the database enforces, and the trade is a migration for every change to the model, which the next section deals with. The template’s build also fails if a public page would query the database on every visit, so readers get pages built in advance and the database works when editors change content.
A managed database adds to the monthly bill, and our Payload pricing guide lists what Neon and MongoDB Atlas charge. If you’d rather a team set up the database and the deploy, our Payload CMS development work covers the data model and the deployment, in your own accounts.
What changes after you pick Postgres or SQLite
On Postgres and SQLite, your tables have to match your Payload config, and the adapter gives you two ways to keep them in step. These are the steps we follow, with the settings our template uses:
- Know the two modes. In development, Payload uses Drizzle’s push to change your tables as soon as you edit a collection, and warns you before a change would lose data. Everywhere else, migrations do the job. Use one or the other on any one database, because push shouldn’t be mixed with running migrate commands by hand.
- Decide whether push stays on. Push is on by default in development. Our template turns it off on every machine, laptops included, so every database gets its schema from
payload migrateand nothing else. With push on, a migration could run for the first time on a client’s server, because local development never ran it. - Create a migration after each model change. Run
payload migrate:create, commit the file it writes, and runpayload migratebefore the new code serves traffic. On a serverless host that’s the build step, and on a long-running server Payload can run them at start-up withprodMigrations, as our guide to hosting Payload explains. Our template’s CI build runspayload migratebeforenext build, so a migration that fails stops the deploy. - Cap the connection pool. Every Payload server holds a pool of database connections. Our template sets the pool’s
maxand aconnectionTimeoutMillisof 10 seconds, because node-postgres otherwise waits for a connection with no time limit, and an exhausted pool once stopped one of our servers until it was restarted. - On Neon, filter out the PostGIS tables. Our Neon databases came with PostGIS system tables already installed and owned by Neon’s
cloud_adminrole, andpayload migratefailed on them with “must be owner of table spatial_ref_sys”. Our template addstablesFilter: ['!spatial_ref_sys', '!geography_columns', '!geometry_columns']to the adapter. - Give each project its own test database. Our template creates one per project. In August 2026, a test database shared between projects held 110 tables, 35 of them another project’s. With push on at the time, every test that started Payload had to reconcile that foreign schema first, and one project’s suite took 696 seconds. On a database of its own, the same suite passed all 406 tests in 88 seconds.
MongoDB skips the schema side of this, because there are no tables to change. A MongoDB project needs a migration only now and then, when existing data has to change shape, such as filling a new field on documents that already exist.
Can you add Payload to an existing database, or switch later?
You can point Payload at an existing Postgres or SQLite database, but Payload won’t take over the tables already in it. By default, when you point Payload at an existing database, it drops the current schema. To keep your tables, generate a Drizzle schema from the database with Drizzle’s introspection and add it through the adapter’s beforeSchemaInit hook. Payload then keeps those tables, and they don’t become collections in the admin.
For example, if the conference’s ticketing system already writes registrations to a Postgres database, that database stays the ticketing system’s. Payload can read from it in a hook or a custom endpoint, while the content Payload manages lives in its own database. When content has to live in a database other systems share, a database-first tool fits better, as our Payload vs Directus comparison explains.
Switching between MongoDB and Postgres later is a data move. Payload has no built-in converter, so the usual route is a script that pages through the old database and writes each document into the new one, as the accepted answer on Payload’s GitHub shows. You change the adapter, then move your content across, which is the same job as moving content into Payload from another CMS.
