Payload CMS multi-tenancy: how it works, how far it goes, and five builds that use it

Idea Labz · Sep 23, 2026 · 16 min read

  • PAYLOAD CMS
  • DESIGN & DEVELOPMENT

You have distributors, business units, clients, or brands, and you want one Payload build to serve all of them. Each one should see only its own data. A few people, like head office, support, or auditors, need to see across all of them. You’d also rather not run a separate install for every party.

Payload handles this with an official Multi-Tenant plugin and the access control it already has. Of the 27 Payload codebases we manage, 13 have multi-tenancy switched on as of September 2026.

This guide covers what multi-tenancy means in Payload, when you need it, how to set it up, how it combines with role-based access down to the field, how far the plugin extends, five builds that use it, and the mistakes to watch for.

  • What multi-tenancy means in Payload
  • Do you need multi-tenancy, or only access control?
  • Start with the plugin
  • Multi-tenancy and role-based access, down to the field
  • How far the plugin extends
  • Five builds on Payload multi-tenancy
  • Mistakes to watch for
  • Where Payload multi-tenancy stops
  • Frequently asked questions
  • What to do next

What multi-tenancy means in Payload

Multi-tenancy in Payload means one database and one codebase serving many parties, with every scoped document stamped with the tenant it belongs to. Every tenant lives in the same database, and a tenant field on each document plus a set of access rules keep one tenant’s data away from another’s.

Payload can do this cleanly because it’s an application framework and backend with a polished admin attached, and access control is part of the core. Our guide to Payload CMS covers how collections, fields, access control, and hooks fit together, if you want that foundation first.

For example, a company with three distributors can run one install. Each distributor’s orders carry that distributor’s tenant, so a distributor user who opens the orders list sees their own orders and nothing else. Head office logs into the same admin and sees all three.

The pieces are few:

  • A tenants collection, one document per tenant, holding its name, slug, and any settings you add.
  • A tenant field on every collection you choose to scope.
  • A tenants array on each user, listing which tenants that user belongs to.
  • Access rules that filter every read and write by the tenants a user belongs to.

The official Multi-Tenant plugin, @payloadcms/plugin-multi-tenant, sets up all four for you.

Do you need multi-tenancy, or only access control?

You need multi-tenancy when data belongs to one party and must stay invisible to the others. You need access control when everyone works on the same data and only their permissions differ. Larger builds usually use both, with tenancy drawing the outer walls and access control deciding what each person may do inside them.

SituationTenancyAccess control
Several brands, each with its own pages, media, and editorsYesLight, per brand
Distributors who see only their own orders, with head office seeing allYesYes, by role and region
One editorial team with writers, editors, and a publisherNoYes
Regional teams editing one shared product catalogueNoYes, by region field
A software product where each customer gets a workspaceYesYes, inside each workspace
Clients whose integrations run on their own API keysYesYes, for who may approve

If the question is “whose data is this?”, that’s tenancy. If the question is “what may this person do with it?”, that’s access control. When you’d otherwise add a brandId or customerId field to every collection and remember to filter on it everywhere, you’re building tenancy by hand. The plugin adds that field and applies the filter for you on every read and write.

Tenancy is the wrong tool when one document belongs to several parties at once. Matija Žiberna’s decision guide on multi-tenancy and access control walks through merging four division websites into one platform, where a single product can sit in two divisions. For that shape, access control with tagging fits better, because each product can carry every division it belongs to.

Start with the plugin

The official plugin is the right starting point for almost every multi-tenant build. It adds the tenant field, a tenant selector in the admin, filtered list views, tenant-scoped relationships, and per-tenant settings. Our ten Payload use cases include a short version of this setup.

Setting it up takes seven steps:

  1. Install the plugin
  2. Create the tenants collection
  3. Give users their tenants
  4. Choose the collections to scope
  5. Add per-tenant settings
  6. Mark your super admins
  7. Connect the frontend and domains

1. Install the plugin

Install @payloadcms/plugin-multi-tenant at the same version as your payload package. As of September 2026 both are at version 3.90.1. If you’re starting from zero, npx create-payload-app --example multi-tenant gives you a working project with tenants, users, and pages already wired.

2. Create the tenants collection

Create a tenants collection with at least a name and a slug. Add a domains field if tenants will have their own domains. Keep create and delete on this collection to super admins only, because the plugin removes a tenant’s documents when the tenant is deleted.

3. Give users their tenants

The plugin adds a tenants array to your users collection. Each row links a user to one tenant, so a user can belong to several. Decide early who may edit that array. In our builds, only super admins can add or change a user’s tenants, which stops a tenant admin from granting themselves access to someone else’s data.

4. Choose the collections to scope

List every collection that holds per-tenant data. Pages, media, forms, and orders are the usual ones. Anything you leave out stays shared across all tenants, which is right for reference data like a product catalogue and wrong for anything private.

5. Add per-tenant settings

Globals are shared by definition, so per-tenant headers, footers, and site settings become collections with isGlobal set to true. The plugin then keeps exactly one document per tenant and skips the list view, so an editor opens their tenant’s footer directly.

6. Mark your super admins

Pass a userHasAccessToAllTenants function that returns true for the people who work across every tenant. The plugin then lets them switch tenants in the admin and skips the tenant filter for them.

7. Connect the frontend and domains

On the frontend, tenant scoping is a query. You filter by tenant.slug, and Next.js rewrites map each tenant’s domain or path to the right tenant. Payload’s localized-multitenant example combines tenant domains with locale paths in one app.

Put together, a config for a small multi-brand build looks like this.

import { multiTenantPlugin } from '@payloadcms/plugin-multi-tenant'

export default buildConfig({
  // ...collections, db, and the rest of your config
  plugins: [
    multiTenantPlugin({
      tenantsSlug: 'tenants',
      collections: {
        pages: {},
        media: {},
        'site-settings': { isGlobal: true },
      },
      tenantsArrayField: {
        includeDefaultField: true,
        arrayFieldAccess: {
          create: superAdminOnly,
          update: superAdminOnly,
        },
      },
      userHasAccessToAllTenants: (user) => Boolean(user?.roles?.includes('super-admin')),
    }),
  ],
})

After setup, when a tenant’s documents are missing from the list view, check the tenant selector and confirm the document carries a tenant. When a user sees the wrong tenant, check their tenants array. When a new document lands in the wrong tenant, check which tenant was selected when it was created.

Multi-tenancy and role-based access, down to the field

In a multi-tenant Payload build, access works at three levels. The tenant decides which documents a user can reach. The role decides what they may do there. The field decides what they may see or change inside a document they can reach.

The tenant decides what a user can reach

The plugin’s filter is the outer wall. For example, in a distributor order portal we built for a hardware manufacturer’s Middle East operation, each distributor is a tenant. A distributor’s users can only reach their own orders, quotations, and cases. There’s no query a distributor can write that returns another distributor’s order, because the plugin combines its tenant filter with your access rules on every read.

The role decides what a user may do, inside and across tenants

Inside the walls, roles set permissions. Some roles live inside one tenant, like a distributor user who can submit orders but not approve them. Other roles cut across tenants on a second axis.

In the same portal, the roles look like this:

RoleTenant scopeRole scopeWhat they may change
Distributor userTheir own distributorTheir own orders and proposalsDraft and submit orders, reply on their orders
Sales specialistEvery distributor in their regionOrders and proposals assigned to their teamMove orders forward, correct orders with the distributor’s confirmation
Sales managerEvery distributor in their regionTheir team’s orders and workloadReassign orders
ExecutiveAll distributorsRead onlyNothing, but sees every region
AdministratorAll distributorsConfigurationItem master, distributor profiles, users, routing rules
AuditorAll distributorsRead onlyNothing

The region is the second axis. A sales specialist isn’t a member of every tenant. Their access is computed from the region on each distributor, so moving a distributor to another region moves its orders to another team without touching a single user record.

The field decides what a user may see or change

Payload lets every field carry its own access functions for create, read, and update.

For example, the licensing platform we’re building has a licence key on each entitlement. The customer who owns the entitlement can read the key, admins can read it, and nobody else can, even staff who can read the rest of the document.

{
  name: 'licenceKey',
  type: 'text',
  access: {
    read: ({ req: { user }, doc }) =>
      isAdmin(user) || doc?.customer === user?.id,
  },
}

In the order portal, a distributor never types a price. Unit prices are resolved from the distributor’s tier and any valid quotation, so the price field is read-only to distributors by construction. In the licensing platform, staff can update an order’s fulfilment fields, but any attempt to change its price is rejected with an access error.

Together, the three levels can express a rule as precise as “a sales specialist in this region can read this distributor’s order and change its shipping details, but not its price.”

How far the plugin extends

The plugin’s defaults fit a multi-brand website as they are. For anything larger, you extend it, and it bends a long way before you need your own access layer. We extend it in this order, and the first two steps use the plugin’s own options.

Mix shared and per-tenant collections

Few builds are entirely per-tenant. A product catalogue, a set of message templates, or a list of exception categories is usually shared, while the orders and cases that use them belong to one tenant. Leave shared collections out of the plugin’s list, or keep them in and set useTenantAccess to false when you want the tenant field but your own access rules.

Replace the default filter where you need to

When the plugin’s filter is too strict for one collection, accessResultOverride lets you adjust its result for that collection alone. It receives the original result and the operation, like read or update, so you can widen reads and leave writes alone. For example, distributors could read the order templates head office publishes for every tenant, while only head office can change them. When you need the tenant field in a particular place in your schema, customTenantField lets you place it yourself.

Carry the tenant into hooks, the Local API, and jobs

The admin panel is only one way in. Server code, background jobs, and payment callbacks also create and read documents, and each one has to know its tenant. For example, in a marketplace where each store is a tenant, an order’s tenant comes from the store that owns the products in the cart, not from the shopper. A guest checkout has no user, but its products still belong to a store, so the order and its invoice land with that store. A cart that holds two stores’ products becomes one order per store, and a hook on order creation is where that split and the tenant stamp happen.

Gate features per tenant

Tenancy can also decide which features a tenant has. The licensing platform unlocks portal modules through entitlements, and every entitlement lookup filters by tenant and fails closed. A module is a route, a navigation item, and a set of tenant-scoped collections, so adding a second tenant needs no changes to any module.

Give each tenant its own domain, locale, and settings

Once the data is scoped, the frontend follows. Each tenant can have its own domain, its own locales, and its own header, footer, and theme settings through isGlobal collections.

Five builds on Payload multi-tenancy

The five builds below all run on the plugin. Each one leans on a different way of extending it. If you’re still choosing a platform, our Payload vs Sanity comparison covers why code-level access control suits builds like these.

1. A distributor order portal with an AI agent in the loop

A hardware manufacturer’s Middle East operation took orders from its distributors by emailed spreadsheets. We built them a portal on Payload where each distributor is a tenant.

Per tenant: orders and their lines, cases, exceptions, messages, quotations, the distributor’s own part-number mappings, and the distributor’s users.

Shared: the item master, message templates, exception categories, and system settings.

What carries the tenant: the region axis for sales teams, a read-across role for executives, and a read-only role for auditors. An AI agent handles routine exceptions, like a quantity below the minimum or an expired quotation, by messaging the distributor inside the case. The agent can only act through defined tools, so it can read the order, draft from an approved template, propose a correction, or escalate, and it can’t send anything free-form. A review switch lets the operations owner hold every agent draft for approval, globally or per exception type. Every state change, message, and agent run is written to an append-only event log, and management watches it all on one dashboard across every distributor.

Best for: manufacturers and wholesalers replacing email and spreadsheet order intake, where distributors must never see each other’s pricing.

2. A licensing platform growing into distributor pricing

A software business sells licences online and is moving to a model where distributors log in, buy, and see their own prices. We’re building it on Payload.

Per tenant: every collection of Payload’s official e-commerce plugin, from products and variants to carts, orders, transactions, and addresses. Entitlements, and the data behind each portal module.

Shared: the platform code and the module definitions.

What carries the tenant: entitlements. Each entitlement grants a licence or a module, sets its own expiry from the product’s term, and holds a licence key that only its owner and admins can read. Distributor-specific pricing is the next phase, and the tenant model is already in place for it. Keep in mind that the e-commerce plugin is still in beta, and it doesn’t handle shipping, taxes, or subscriptions for you.

Best for: software and content businesses that sell licences and plan to sell through resellers or distributors.

3. Integrations per tenant: invoicing, CRM, and messaging

When each client or business unit has its own accounts with outside systems, the integration data becomes per-tenant data. We scope it that way on every build.

Per tenant: credentials for each outside system, the workflows that use them, pending approvals, execution logs, and inbound messages.

Shared: the integration code.

What carries the tenant: the workflow. Each workflow belongs to one tenant and points at a credential picked from that tenant’s own credentials, since the plugin scopes the picker too. So when a background job raises an invoice or updates a CRM record, it runs on that client’s account. A workflow can pause at an approval step, which parks the action in a pending-approvals collection until a person approves it, and the execution log records what ran and what came back.

Best for: agencies and groups that run automations for several clients or companies from one backend.

4. AI assistants with their own knowledge and tools

An assistant that answers for one brand must never quote another brand’s documents. In our voice-agent builds, the agents, their knowledge, their tools, and their call logs are all tenant-scoped collections.

Per tenant: agent definitions, the knowledge they answer from, the tools they may call, and the log of every tool call.

Shared: the agent runtime.

What carries the tenant: the agent. Each agent belongs to one tenant, and every knowledge lookup and tool call has to carry that tenant into its query. Collection scoping protects the admin panel, but an agent’s lookup usually runs through the Local API from a server route. For example, a knowledge search needs the agent’s tenant in its where clause, or it reads every tenant’s entries that match the query.

Best for: teams offering AI assistants to several clients or brands, where each assistant’s knowledge is confidential.

5. Team workspaces and internal tools

Internal tools often need the tenant to be a team rather than a customer. We run a workspace product on Payload where each team is a tenant.

Per tenant: workspaces, memberships, invites, shared databases and their rows, automations, and team settings.

Shared: the product itself.

What carries the tenant: memberships. A person joins a team by invite, and whatever the team shares, like a database or an automation, is shared only inside that team. Employee dashboards follow the same pattern, with departments or companies in a group as tenants and a read-across role for leadership.

Best for: internal tools, employee portals, and team products where departments or subsidiaries must keep their data apart.

The five builds at a glance

BuildThe tenant isPer tenantSharedCarries the tenant
Distributor order portalA distributorOrders, cases, quotations, messagesItem master, templatesRegion axis, tool-bound agent, event log
Licensing platformA business, distributors nextCommerce collections, entitlementsPlatform, module definitionsEntitlements
IntegrationsA client or business unitCredentials, workflows, approvals, logsIntegration codeThe workflow and its credential
AI assistantsA client or brandAgents, knowledge, tools, logsAgent runtimeThe agent’s tenant, in every lookup
Team workspacesA team or companyWorkspaces, memberships, shared dataThe productMemberships

If one of these is close to what you’re planning, the tenant model is the decision to settle first. We cover how we shape that on our Payload development service page.

Mistakes to watch for

Multi-tenant bugs tend to be quiet. Nothing crashes, and one tenant sees something it shouldn’t, or can’t see something it should.

A Local API call that skips access control

Payload’s Local API skips access control by default. A server-side lookup that doesn’t set overrideAccess to false can return another tenant’s document. For example, in the licensing platform’s customer portal, an order page fetched this way rendered another customer’s order during testing. The fix was one line, plus a test that proves someone else’s order returns a not-found page.

const order = await payload.findByID({
  collection: 'orders',
  id,
  user,
  overrideAccess: false,
})

A server call made with a user who has no tenants

Pass the full user document into Local API calls, tenants included. A trimmed user object without its tenants array gets no access at all to tenant-scoped collections, so every read is refused. In the same portal, the session helper returned a partial user, and every order query failed until the pages read the full user instead.

A new collection left off the plugin’s list

A collection you add later isn’t scoped until you add it to the plugin’s collections. Until then it’s shared across every tenant. Make registration part of the checklist for any new collection.

Slugs that are unique across the whole database

Two tenants will both want an about page. Make slugs unique per tenant with a compound unique index on the tenant and slug fields, so each tenant can publish its own about page.

Self-registration that creates a user with no tenant

If customers can sign up themselves, the new user needs a tenant at creation, usually the store or site they signed up on. Set it in a hook from the domain the request came in on, or the user logs in to an empty account.

A custom tenant field that doesn’t recognise super admins

If you place the tenant field yourself with customTenantField, pass your userHasAccessToAllTenants function to it as well. Otherwise, super admins’ writes to tenants they aren’t directly assigned to are rejected.

Weak access on the tenants collection

Deleting a tenant deletes its documents by default. Keep delete on the tenants collection to super admins, and turn off cleanupAfterTenantDelete if you’d rather archive a tenant than remove its data.

Where Payload multi-tenancy stops

The plugin gives you logical isolation inside one database, which covers most builds.

Every tenant shares the same schema. If one tenant needs fields or collections the others must not have, you either add them for everyone and hide them, or you run that tenant separately.

Every tenant shares the same database. When a contract or a data-residency rule requires a client’s data to sit in its own database or its own region, that client gets its own deployment of the same codebase.

Every query filters by tenant. The plugin already indexes the tenant field, so as tenants grow, add compound indexes for the fields you query alongside it, like tenant and slug, and watch your slowest queries.

Frequently asked questions

Is Payload multi-tenancy free?

Yes. Payload and its official Multi-Tenant plugin are open source under the MIT licence, so there’s no charge per tenant. Your costs are hosting, the database, and file storage, and they grow with usage.

Can one user belong to several tenants with a different role in each?

Yes. The tenants array on each user can hold several tenants, and Payload’s multi-tenant example gives each row its own roles, so a person can be an admin in one tenant and an editor in another.

Can some content be shared across all tenants?

Yes. Leave a collection off the plugin’s list and it’s shared. For a collection that is mostly per tenant but needs some shared documents, keep it in the list, set useTenantAccess to false, and write your own access rule.

Does the Payload e-commerce plugin work with multi-tenancy?

Yes, in our experience. Our licensing platform scopes every collection of the official e-commerce plugin by tenant. The plugin is in beta as of September 2026. In a marketplace, take each order’s tenant from the store its products belong to, so guest checkouts land with the right store, and split a cart that holds several stores’ products into one order per store.

Can Payload use a separate database for each tenant?

Not with the plugin, which keeps every tenant in one database. If a tenant needs its own database, run a separate deployment of the same codebase for that tenant.

Can I add multi-tenancy to an existing Payload project?

Yes. Install the plugin, create the tenants collection, and run a migration that assigns every existing document and user to a tenant before you switch the filter on. Otherwise, existing documents without a tenant disappear from the lists.

Is there a GitHub example of a multi-tenant Payload app?

Yes, two. npx create-payload-app --example multi-tenant creates Payload’s official example, and the localized-multitenant repo shows tenants on their own domains with localised paths.

How does Payload’s multi-tenancy compare with Strapi’s?

Payload ships multi-tenancy as an official plugin maintained with the core, so many tenants share one install. Strapi takes the other route. Its own guide to multi-tenancy in Strapi says the core intentionally doesn’t support true multi-tenancy in a single instance, and recommends one Strapi project per client, site, or application. If you want separate installs per client, Strapi’s model fits that, and if you want one install with walls inside it, Payload’s plugin does.

What to do next

Start by deciding whether your problem is tenancy, access control, or both, using the situations table. Then scaffold the official example and scope two collections before you scope ten. Pick the extensions your build needs, and run the mistakes list as a test plan before real data goes in.

For the rest of Payload, from collections and hooks to hosting costs, read our Payload CMS features and architecture guide next. If you’d rather start building, the official multi-tenant example from the setup steps is the quickest way in.

References

All links checked and active as of 23 September 2026.

READY TO MAKE A REAL CHANGE?

Let's build it together