Payload CMS 3 to 4: what to audit before upgrading
Payload 4.0.0-beta.4 is the latest prerelease; Payload 3.92.0 is the latest stable release. What to inventory before upgrading, an impact matrix for changes that alter behaviour without an error, and what happened when we ran a fixture on beta.2.
As checked on 24 October 2026, Payload 3.92.0 is the latest stable release and 4.0.0-beta.4 is the latest prerelease. Both were published on 22 October. Use the beta to identify what a future upgrade will touch; keep production on the stable line until a stable 4.0 is available. The migration guide listed 97 breaking changes when we reviewed it on 19 October, including changes to working behaviour that do not raise an error.
Check the runtime before either update. Payload 3.92.0 raises its Node requirement to 22.23.4 or later within Node 22, or 24.22.1 and newer (^22.23.4 || >=24.22.1). Beta.4 requires Node 24.22.1 or newer. Check local development, CI and hosting together. Beta.4 also fixes global version restoration and preservation of published content when restoring drafts, alongside admin accessibility fixes. These are vendor-reported changes; our fixture results below remain specific to beta.2.
Beta.3 introduced cache component support, validation of each locale when writing localized fields with locale: 'all', and a breaking TanStack Start change using React Flight for RSC responses. The locale-validation fix is also included in stable 3.92.0.
This is our audit list for teams on Payload 3. What Payload documents and what we tested are kept apart, and each row of the impact matrix links to its section of Payload’s 3.0 to 4.0 migration guide, read on 19 October 2026. WAYF is an official Payload partner; we start migrations on the stable line and move clients to the next major once it is stable.
What to inventory on Payload 3 now
- Every Local API call (
payload.find,create,update,delete,countand thepayload.jobsoperations), and whether each one acts for a user or as trusted server code. Payload 4 makes these calls enforce access control unless they passoverrideAccess: true. - Queries that rely on two populated relationship levels without setting
depth. - Collections and globals with no
versionssetting, and any custom auth collection. Payload 4 turns versions and authorship on by default, which on SQL databases means a migration. - Public collections without
readVersions, because version access now inherits read access. - Integrations that hold Payload tokens or API keys, and API keys created before 3.46.0 and never re-saved.
- Background jobs: calls that queue or run jobs, cron expressions, and any custom database adapter.
- Runtimes: Node, Next.js and TypeScript versions in local development, CI, Docker images and hosting.
- Rich text and uploads: Slate editor fields, legacy Lexical HTML converters, storage adapters registered under
plugins, the Uploadthing adapter, and code that depends on uploaded file names. - Scripts the codemod may not read, such as
.mjsor.cjsseeds and migration scripts.
The impact matrix
These six areas change behaviour in ways a successful build does not reveal. Payload’s guide covers the rest, from admin components to Lexical types, and many of those are renamed or removed APIs that a build or type check reports.
The rollback column describes each change in isolation. Reverting the full upgrade also requires compatible application code, runtime and database state. Check down migrations before relying on them; restoring a pre-upgrade backup can discard later writes.
| Change in Payload 4 | Automated help | Inspect and validate | Rollback |
|---|---|---|---|
Local API access. overrideAccess now defaults to false, so calls that omitted it enforce access control, with no compile error. | The codemod adds overrideAccess: true to calls that omit it; spread, aliased and non-literal arguments are left for you. | Review every inserted value; set false where the call acts for a user. Test cross-user reads, seeds, hooks and jobs. | Code only: redeploy the previous build. |
Auth tokens and API keys. Tokens without the new authVersion header marker are rejected; API keys created before 3.46.0 and never re-saved stop authenticating. | None. After an upgrade from an unpatched release, users sign in again; holders of old API keys re-save their user document. | Which Payload 3 release issued current tokens, which integrations store them, and which keys predate 3.46.0. Test sign-in and each integration after deploying. | Sessions ended by the upgrade stay ended; warn users and integration owners. |
| Jobs. Job operations default to enforcing jobs access; the jobs collection denies generic access; cron expressions need explicit ranges. | The codemod adds overrideAccess: true to jobs calls, including payload.jobs.run(). | Run each task, workflow and schedule on staging, and confirm the jobs access rules. | Code and configuration: redeploy the previous build. |
Query depth. Queries that omit depth populate one relationship level instead of two. | None. Set depth: 2 where needed, or defaultDepth: 2 for the whole config. | Compare API responses and rendered pages that show nested relationships. | Configuration: one setting. |
| Versions and authorship. Both are on by default; on SQL adapters each needs a database migration, and version reads inherit read access. | Transforms add versions: false and authorship: false; in our run only the authorship one changed our config. | Generate the migration with payload migrate:create and read it; check readVersions on public collections. | Schema: restore the pre-migration backup or run the down migration. |
| Runtimes. As of beta.4, Node 24.22.1 is required; the guide also specifies Next.js 16.4.0 and TypeScript 6.0.3 as minimums. | None. | CI, Docker images and hosting on the new versions. | Keep the previous runtime image until the release is accepted. |
Three smaller changes belong on the same review: oversized uploads now return HTTP 413 instead of being truncated, the Admin UI publishes the active locale by default, and storage adapters move from plugins to a top-level storage key.
What we tested
On 19 October 2026 we ran one of our own fixtures on 4.0.0-beta.2: the sixteen tests from Building field-level RBAC in Payload CMS, which check that a role policy holds on API requests. The fixture is disposable: Payload with the SQLite adapter, no admin panel and no Next.js, in plain JavaScript modules, run with Node v24.22.0 on Windows. It covers access control and says nothing about the admin UI, rich text, uploads or a production database.
- Before. Payload 3.90.2 and
@payloadcms/db-sqlite3.90.2:npm ci,npm run seedandnpm run verifypassed 16 of 16 tests. - Dependencies only. npm refused to upgrade in place, because the 4.0.0-beta.2 SQLite adapter declares
payload@4.0.0-beta.2as an exact peer dependency, so the Payload packages have to move together. After changing both versions inpackage.jsonand reinstalling without the old lockfile,npm run seedstopped at its firstcreatewithForbidden: You are not allowed to perform this action., a 403. The seed depended on Payload 3’s Local API default. - The codemod.
npx @payloadcms/codemod@4.0.0-beta.2 .reported 0 files changed for every transform. Without atsconfig.json, its file pattern is*.ts,*.tsx,*.jsand*.jsx, and the fixture’s files are.mjs. - The codemod on
.jscopies. After renaming the three files to.js, it addedoverrideAccess: trueto 18 Local API calls andauthorship: falseto 3 collections. The versions transform changed nothing. The seed ran, and 16 of 16 tests passed. - The schema. The database built on 4.0.0-beta.2 had version tables for all three collections, including the custom users auth collection, which Payload’s guide says to set to
versions: false. It also had twopayload_llm_instructionstables that the fixture does not configure.
The tests passed because the codemod restored Payload 3’s access bypass explicitly. One of the 18 inserted values sits on a call that passes a user and reads another tenant’s document. With overrideAccess: true it still bypasses access control, the case Payload’s guide says to review and switch to false. A green suite after the codemod shows that behaviour was preserved. Whether it should have been preserved is a decision for the review. Our approval workflow example was written against the same Payload 3 default, and we did not run it on the beta.
Planning the production upgrade
Start on a branch, with a copy of production data.
- For a beta.4 rehearsal, move Node to 24.22.1 or later in CI, Docker and hosting, and update Next.js and TypeScript to the guide’s minimums. Recheck the requirements of the exact release before the production upgrade.
- Bump every
payloadand@payloadcmspackage to the same version, runnpx @payloadcms/codemod, then run a type check, which the guide recommends for the cases the codemod leaves behind. - Review each inserted
overrideAccess: true, and read the generated database migrations before running them. - Test sign-in, every integration that stores a token or API key, and each background job.
- Take a database backup before the migrations run, and keep the previous build and runtime image ready.
Payload’s 4.0 announcement set the goal of a shorter path from beta to stable than version 3 had, without giving a date. In a maintenance arrangement, a major upgrade is planned scope, priced and scheduled apart from support; WAYF’s audit and ongoing retainer keeps those tracks separate.
FAQ
-
Is Payload 4 stable yet?
As checked on 24 October 2026, Payload 3.92.0 is the latest stable release. Payload 4.0.0-beta.4 is a prerelease under npm's beta tag. Both were published on 22 October. Payload has announced no date for a stable 4.0.
-
Does the Payload 4 codemod migrate a project automatically?
It migrates many changes and leaves others for review. Payload's guide says most breaking changes are auto-migratable with npx @payloadcms/codemod and lists cases it leaves behind. In our run on 4.0.0-beta.2 it skipped .mjs files, and the overrideAccess: true values it inserted kept Payload 3's access bypass on every Local API call, including one made on behalf of a user, which is the case the guide says to review.
-
Will users have to sign in again after upgrading to Payload 4?
When the site upgrades directly from a Payload release without the JWT fix, yes. Payload 4 rejects tokens that lack its authVersion header marker, so users sign in again and integrations reissue stored tokens. Tokens issued by a patched Payload 3 release stay valid if the signing secret and the auth configuration stay compatible.
Sources
- Payload, 3.0 to 4.0 migration guide (beta documentation), read 19 October 2026: the codemod, Local API and jobs access defaults, JWT versioning, API keys, query depth, versions, version read access, authorship, uploads, localisation and storage adapters. Runtime requirements rechecked 24 October 2026.
- Payload on GitHub, v4.0.0-beta.3 release notes, published 19 October 2026: cache component support, localized-field validation for
locale: 'all', and the breaking TanStack Start React Flight change - Payload on GitHub, v3.92.0 release notes and v4.0.0-beta.4 release notes, published 22 October 2026; npm dist-tags checked 24 October 2026:
latest3.92.0 andbeta4.0.0-beta.4. Node engine constraints checked in the tagged 3.92.0 and beta.4 manifests. - Payload, 4.0 announcement, read 19 October 2026
- Our run of the RBAC fixture on 19 October 2026: Payload 3.90.2 and 4.0.0-beta.2 with the matching SQLite adapter,
@payloadcms/codemod4.0.0-beta.2, Node v24.22.0, npm 11.19.1. The beta.2 fixture results above are version-specific; beta.3 and beta.4 were not tested.
We're booking content platform
engagements for 2026.
Twenty-five minutes to walk through the work and decide if we're the right team for it. Scoping and a fixed price come after.