Docs

This page isn't translated yet

Deploying

Production builds, building without database access, Payload version policy and sitemap caching.

On this page

Run a Systhema project in production: start commands, Docker builds, builds that cannot reach the database, and the Payload versions the bundled patch supports.

Production deploymentLink to this section

On Railway, set the start command to node_modules/.bin/next start. Next must receive SIGTERM itself during a redeploy. Do not use pnpm start as the Railway start command. Run database migrations in Railway's pre-deploy command instead of the start command.

In a multi-stage Docker build, copy .systhema/ before or with the install stage so its artifact mirror is available. If you regenerate artifacts during the image build, copy the generated .systhema/ into the runtime stage too. Do not depend on a runtime sync unless you explicitly configure one.

Building without database accessLink to this section

By default next build prerenders every published page and post, so the build needs the database. Some hosts only expose it on a private network that the builder cannot reach. On Railway, for example, postgres.railway.internal does not resolve during the build, and the build fails at "Collecting page data" with ENOTFOUND.

Set SYSTHEMA_PRERENDER=off in the build environment instead of exposing the database publicly:

SYSTHEMA_PRERENDER=off pnpm build

generateSysthemaStaticParams then returns no params and never starts Payload. Each page renders on its first request and stays cached for the route's revalidate period, and publishing still revalidates it. The trade-off is that the first visitor to each page after a deploy waits for a server render. false and 0 work as well as off.

The generated routes avoid the other build-time database reads described below. Review your custom routes and template data hooks separately. The sitemap, robots.txt, /sys/*, Admin and API routes are dynamic, and the not-found page and icons need no data. This flag removes the managed catch-all's prerender query; it does not guarantee success if project-owned code connects during the build.

The catch-all page.tsx must hand generateSysthemaStaticParams a getter, payloadInstance: () => getPayload({ config: configPromise }). A getPayload() promise has already started connecting by the time the flag is checked. systhema upgrade refreshes an unedited page.tsx. If you patched it to return [], take the new template from page.tsx.new and set the variable instead.

Payload version policyLink to this section

The bundled Lexical patch supports @payloadcms/richtext-lexical ^3.90.0. Keep payload and every @payloadcms/* package on compatible caret ranges such as ^3.90.1. The patch file is named for its floor, and pnpm applies it across the supported minor. Run systhema doctor after dependency changes. The range is the patch's declared support; it does not prove every future release will accept the diff. Exact family pins are also supported when they remain aligned. Use Upgrading a client site to review the patch and migration plan.

Sitemap route cachingLink to this section

The managed sitemap Route Handler uses export const dynamic = 'force-dynamic', not time-based ISR. Its Cache-Control response header already provides shared caching and stale-while-revalidate behavior. Keeping it dynamic avoids a database read during next build. In the generated routes, the catch-all prerender path queries the database during the build; see Building without database access.