Payload CMS Configuration: Required Parameters and Production Setup

Topic: payloadUpdated 7/22/2026

Quick Answer

  • Minimum viable config: You must provide secret (a strong random string for encryption) and db (a database adapter like @payloadcms/db-mongodb or @payloadcms/db-postgres). Without these two, Payload will not start.
  • First check: Ensure payload.config.ts exists in your project root, or set PAYLOAD_CONFIG_PATH environment variable to point to it. Verify your database is running and accessible.
  • Production essentials: Set serverURL to your absolute domain (protocol + domain, no paths), configure CORS and CSRF for security, and use cloud storage (S3/GCS) instead of local filesystem for media uploads.
  • Version boundary: Payload 3.x uses the configuration structure described here. If upgrading from Payload 2.x, note that serverURL is no longer required but strongly recommended for email and admin panel features.

What Problem It Solves

Payload CMS solves the problem of building a custom, code-first content management system that gives developers full control over data models, API endpoints, and admin UI without sacrificing developer experience. Unlike UI-first CMS platforms, Payload lets you define everything in TypeScript configuration files, which means:

  • Configuration is version-controlled and reviewable in pull requests
  • Full type safety with auto-generated TypeScript types
  • No lock-in to a specific UI paradigm
  • Easy CI/CD integration
  • Local API access without HTTP overhead

Minimal Working Configuration

Here's the smallest possible Payload configuration that will start a server:

TYPESCRIPT
// payload.config.ts
import { buildConfig } from 'payload'
import { mongooseAdapter } from '@payloadcms/db-mongodb'
import { slateEditor } from '@payloadcms/richtext-slate'

export default buildConfig({
  secret: process.env.PAYLOAD_SECRET || 'your-secret-key',
  db: mongooseAdapter({
    url: process.env.DATABASE_URI || 'mongodb://127.0.0.1:27017/your-db',
  }),
  editor: slateEditor({}),
  collections: [],
})

To run this, ensure you have the required packages installed:

BASH
npm install payload @payloadcms/db-mongodb @payloadcms/richtext-slate

Then start with:

BASH
npx payload

Parameters and Environment Variables

The buildConfig() function accepts a configuration object with the following key parameters:

ParameterRequiredDescription
secretYesStrong random string for encryption, password hashing, and JWT signing. Never commit to version control.
dbYesDatabase adapter instance. Use @payloadcms/db-mongodb or @payloadcms/db-postgres.
serverURLNoAbsolute URL of your app (e.g., https://example.com). No paths allowed. Required for email and admin panel features.
collectionsNoArray of Collection configurations for content types.
globalsNoArray of Global configurations for singleton content.
corsNoArray of allowed origins for CORS.
csrfNoArray of URLs allowed to accept cookies from.
adminNoAdmin panel configuration including custom components and live preview.
editorNoRich text editor configuration. Defaults to Slate if not provided.
localizationNoEnable multi-locale content with locale configuration.
graphQLNoGraphQL-specific configuration including custom queries and complexity limits.
emailNoEmail adapter configuration for transactional emails.
uploadNoBase upload configuration for media handling.
pluginsNoArray of Payload plugins.
hooksNoArray of root-level hooks.
endpointsNoCustom REST endpoints added to the Payload router.
debugNoEnable detailed error information in responses.
telemetryNoSet to false to disable anonymous telemetry.
defaultDepthNoDefault relationship depth if user doesn't specify.
maxDepthNoMaximum allowed relationship depth (default: 10).
cookiePrefixNoPrefix for all cookies set by Payload.
routesNoCustomize URL routes that Payload binds to.
i18nNoInternationalization configuration for admin UI languages.
sharpNoPass the Sharp module for automatic image resizing and cropping.
typescriptNoTypeScript generation settings.
customNoExtension point for custom data (useful for plugins).

Environment Variables

Common environment variables used with Payload:

BASH
PAYLOAD_SECRET=your-strong-random-secret
DATABASE_URI=mongodb://127.0.0.1:27017/your-database
PAYLOAD_CONFIG_PATH=./src/payload.config.ts
PAYLOAD_PUBLIC_SERVER_URL=https://example.com

Root Cause Analysis

Why secret and db are Required

Payload uses the secret for multiple security-critical operations:

  • Password hashing: User passwords are salted and hashed using this secret
  • JWT signing: Authentication tokens are signed with this secret
  • Encryption: Any field-level encryption uses this secret as part of the key derivation

The db parameter is required because Payload is a database-driven CMS. Without a database adapter, there's no storage layer for content, users, or configuration. The adapter handles:

  • Connection pooling and lifecycle
  • Query translation from Payload's API to database-specific queries
  • Migration management
  • Transaction support (PostgreSQL adapter)

Why serverURL Matters Even Though It's Optional

While serverURL is technically optional, omitting it breaks several features:

  • Email functionality: Password reset emails and other transactional emails need an absolute URL to generate links
  • Admin panel: Some admin features like preview URLs and redirects depend on knowing the server URL
  • CORS/CSRF: The server URL is used as a default allowed origin

Common Errors and Fixes

Error: Cannot find module 'payload'

Cause: Payload package not installed or not in node_modules.

Fix:

BASH
npm install payload
# or
yarn add payload
# or
pnpm add payload

If using pnpm, ensure your .npmrc has shamefully-hoist=true or use pnpm install --shamefully-hoist.

Error: Payload config not found

Cause: Payload cannot locate payload.config.ts in the project root.

Fix: Either move the file to the project root, or set the PAYLOAD_CONFIG_PATH environment variable:

BASH
PAYLOAD_CONFIG_PATH=./src/config/payload.config.ts npx payload

Error: MongoNetworkError: connect ECONNREFUSED 127.0.0.1:27017

Cause: MongoDB is not running or the connection string is wrong.

Fix:

  1. Start MongoDB: mongod (or use Docker: docker run -d -p 27017:27017 mongo)
  2. Verify your DATABASE_URI or db configuration URL is correct
  3. For MongoDB Atlas, check IP whitelist and network access settings

Error: Forbidden - You are not allowed to perform this action.

Cause: Access control rules are blocking the request.

Fix: Check your collection or global access configuration. For public read access, ensure:

TYPESCRIPT
access: {
  read: () => true, // Allow public read
  create: () => true, // Allow public create (if needed)
}

Production Notes and Security Checks

Critical Production Requirements

  1. Secret management: Never hardcode secret. Use environment variables or a secrets manager:

    BASH
    # Generate a strong secret
    openssl rand -base64 32
    
  2. Database: Use a managed database service (MongoDB Atlas, AWS RDS for PostgreSQL) with:

    • SSL/TLS enabled
    • IP whitelisting or VPC peering
    • Automated backups
  3. Media storage: Replace local filesystem with cloud storage:

    TYPESCRIPT
    import { s3Adapter } from '@payloadcms/plugin-cloud-storage/s3'
    
    export default buildConfig({
      // ...
      plugins: [
        cloudStorage({
          collections: {
            media: {
              adapter: s3Adapter({
                config: {
                  region: process.env.S3_REGION,
                  credentials: {
                    accessKeyId: process.env.S3_ACCESS_KEY_ID,
                    secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
                  },
                },
                bucket: process.env.S3_BUCKET,
              }),
            },
          },
        }),
      ],
    })
    
  4. CORS and CSRF: Restrict to your frontend domain:

    TYPESCRIPT
    export default buildConfig({
      // ...
      cors: ['https://your-frontend.com'],
      csrf: ['https://your-frontend.com'],
    })
    
  5. Depth limits: Set maxDepth to a reasonable value (2-3) to prevent performance issues from deep relationship queries.

Concurrency and Data Integrity

Payload does not implement optimistic or pessimistic locking by default. In high-concurrency scenarios (multiple editors modifying the same document), enable the versions feature:

TYPESCRIPT
const Posts = {
  slug: 'posts',
  versions: {
    drafts: true,
    maxPerDoc: 10,
  },
  // ...
}

This creates a draft/version history that prevents data loss during concurrent edits.

FAQ

Q: How do I choose between MongoDB and PostgreSQL for Payload?

A: Choose MongoDB if you need flexible schemas, rapid iteration, and complex nested relationships. Choose PostgreSQL if you require strict data integrity, SQL-based reporting, or need to integrate with existing relational databases. Note that the PostgreSQL adapter is newer and some features (like advanced localization) may be less mature than the MongoDB adapter.

Q: Can I use Payload with Next.js?

A: Yes, Payload has first-class Next.js support. The recommended approach is to run Payload as an embedded API within your Next.js app using the @payloadcms/nextjs package. This gives you access to Payload's local API directly in your server components and route handlers without HTTP overhead.

Q: How do I handle file locking when scaling horizontally?

A: Never use the local filesystem for media uploads in production. Always use a cloud storage adapter (S3, GCS, Azure Blob) with the @payloadcms/plugin-cloud-storage plugin. This ensures all instances share the same storage backend and avoids file locking issues.

Related Guides