Skip to content

payload build requires runtime secrets because it loads the full config (Payload 3 & 4) #18497

Open

Description

@Llois41

Describe the Bug

payload build imports payload.config.ts in full before it runs the framework build. It does this through runtime.getConfig(): first to load the CLI commands, then for generateImportMap and generateTypes.

A config that validates its environment when it is imported therefore breaks the build in CI. Examples are throwing on a missing PAYLOAD_SECRET or DATABASE_URL, or on any value the app requires. In CI, runtime secrets are deliberately absent. The framework build that follows (vite build on TanStack Start) never evaluates the config, so only the generate step needs it.

Today there are two workarounds, and neither is good:

  • Placeholder values in the build environment. Every required variable needs a fake value, only so the config can load.
  • A config that detects it is being built. Build-specific branches end up in production code.

Payload 3 had the same problem with next build, and the docs describe a workaround for it: https://payloadcms.com/docs/production/building-without-a-db-connection

Expected behavior

The build-time steps (import map, types) can run without runtime configuration. Some options:

  • A documented, supported way to build without runtime values, for example:
    • the CLI loads the config in a mode where secret and db aren't required, or
    • buildConfig takes them lazily, so they are only read when Payload initialises.
  • A flag such as payload build --skip-generate that runs only the framework build, so the generated files can come from a separate step that has the secrets, or be committed.

Link to the code that reproduces this issue

https://github.com/Llois41/payload-build-requires-secret

Reproduction Steps

  1. In payload.config.ts, throw when process.env.PAYLOAD_SECRET is unset.
  2. Run payload build with no PAYLOAD_SECRET in the environment.
  3. The build fails during config loading, before vite build starts.

Which area(s) are affected?

area: core

Environment Info

### Versions

- `payload` 4.0.0-canary.37
- `@payloadcms/tanstack-start` 4.0.0-canary.37
- `@payloadcms/db-postgres` 4.0.0-canary.37
- Node 26

Activity

  1. added
    status: needs-triagePossible bug which hasn't been reproduced yet
    validate-reproductionAuto-added tag on create to tell bot to check recreation URL, removed after check.
    on Oct 5, 2026
  2. added
    area: coreCore Payload functionality
    and removed
    validate-reproductionAuto-added tag on create to tell bot to check recreation URL, removed after check.
    on Oct 5, 2026
  3. Llois41 commented on Oct 5, 2026

    @Llois41
    Author

    update: not only payload build, but every CLI command.

    The CLI imports the full config while it starts up (createCLIRuntime → getConfig(), called from loadCLICommands), before any command is chosen. I updated the reproduction repo, which now shows:
    without .env, all of these fail with Missing runtime configuration: PAYLOAD_SECRET, DATABASE_URL:

    pnpm exec payload --help
    pnpm exec payload generate:importmap
    pnpm exec payload generate:types

    So --skip-generate alone wouldn't fix it. A CI job that only checks whether the committed importMap.js is up to date (generate:importmap + diff) needs placeholder secrets too. Commands that open a database (migrate*) clearly need real values. Commands that only read the config's structure (import map, types, help) shouldn't.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Bugarea: coreCore Payload functionalitystatus: needs-triagePossible bug which hasn't been reproduced yetv3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions