6.2 KiB
6.2 KiB
| title | aliases | tags | sources | created | updated | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Production Deployment |
|
|
|
2026-05-15 | 2026-05-15 |
Overview
Payload runs fully inside Next.js — deploy anywhere Next.js runs (Vercel, Netlify, DigitalOcean, AWS, Cloudflare Workers, SST, self-hosted). Every project also needs:
- A database (Postgres or MongoDB)
- File storage (persistent or cloud)
- An email provider
- Optionally a CDN
Basics
Build via the build npm script (wraps next build). Start via next start. This is not next dev.
npm run build # or pnpm build
npm run start # next start
Security Checklist
- Secret key — must be long, complex, impossible to brute-force. Never commit to git.
- Access Control — audit every collection before going live. By default all CRUD requires a logged-in user. Public registration loosens this — tighten explicitly.
- Secure cookies — enable when using SSL (production always should).
- Anti-abuse — built-in: login lockout after N failures, GraphQL complexity limits, max
depth. See wiki/payloadcms/preventing-abuse.
Database
Set DATABASE_URL env var. Supported:
| Engine | Notes |
|---|---|
| Postgres | Preferred; full feature support |
| MongoDB | Mongoose adapter |
| AWS DocumentDB | Configure connectOptions in mongooseAdapter |
| Azure Cosmos DB | Use compatibilityOptions.cosmosdb preset (see below) |
Cosmos DB Compatibility Preset
import { mongooseAdapter, compatibilityOptions } from '@payloadcms/db-mongodb'
export default buildConfig({
db: mongooseAdapter({
url: process.env.DATABASE_URL,
...compatibilityOptions.cosmosdb,
indexSortableFields: true,
}),
})
| Option | Value | Purpose |
|---|---|---|
bulkOperationsSingleTransaction |
true |
One-at-a-time bulk ops (Cosmos limits) |
transactionOptions |
false |
Multi-doc transactions unsupported |
useJoinAggregations |
false |
Multiple finds instead of subqueries |
usePipelineInSortLookup |
false |
Disable $lookup pipeline in sort |
File Storage
Ephemeral vs Persistent Filesystems
| Ephemeral (uploads lost on restart) | Persistent |
|---|---|
| Heroku | DigitalOcean Droplets |
| DigitalOcean Apps | Amazon EC2 |
| GoDaddy |
If using uploads on ephemeral hosts → must use a cloud storage plugin.
Official Cloud Storage Adapters
@payloadcms/storage-s3— AWS S3@payloadcms/storage-azure— Azure Blob Storage@payloadcms/storage-gcs— Google Cloud Storage@payloadcms/storage-vercel-blob— Vercel Blob Storage@payloadcms/storage-uploadthing— Uploadthing
See wiki/payloadcms/upload for adapter config details.
Docker
next.config.js
const nextConfig = {
output: 'standalone',
}
Multi-Stage Dockerfile
FROM node:24-alpine AS base
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN \
if [ -f yarn.lock ]; then yarn run build; \
elif [ -f package-lock.json ]; then npm run build; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
else echo "Lockfile not found." && exit 1; \
fi
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
RUN mkdir .next && chown nextjs:nodejs .next
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
CMD HOSTNAME="0.0.0.0" node server.js
Required env vars at runtime: PAYLOAD_SECRET, PAYLOAD_CONFIG_PATH, DATABASE_URL.
Docker Compose (Development)
version: '3'
services:
payload:
image: node:24-alpine
ports:
- '3000:3000'
volumes:
- .:/home/node/app
- node_modules:/home/node/app/node_modules
working_dir: /home/node/app/
command: sh -c "corepack enable && corepack prepare pnpm@latest --activate && pnpm install && pnpm dev"
depends_on:
- mongo
env_file:
- .env
mongo:
image: mongo:latest
ports:
- '27017:27017'
command: --storageEngine=wiredTiger
volumes:
- data:/data/db
logging:
driver: none
volumes:
data:
node_modules:
For Postgres: uncomment the postgres service and use DATABASE_URL=postgres://....
Key Takeaways
- Payload deploys anywhere Next.js does — the build process is
next build. - Always use a long, random secret key in production; double-check access control before launch.
- Ephemeral filesystems (Heroku, DigitalOcean Apps) lose uploads on restart — use S3/GCS/Azure instead.
- Use
output: 'standalone'in next.config.js for Docker builds. - The multi-stage Dockerfile works across npm/yarn/pnpm automatically via lockfile detection.
- Azure Cosmos DB needs
compatibilityOptions.cosmosdbspread intomongooseAdapterfor reliable operation. - For Docker Compose dev: database hostname in
DATABASE_URLmust match the service name (e.g.mongo).
Related
- wiki/payloadcms/building-without-db-connection — Docker build failures when SSG calls Local API
- wiki/payloadcms/upload — storage adapter config details
- wiki/payloadcms/performance-overview — DB proximity, indexes, caching
- wiki/payloadcms/environment-vars —
.envsetup for Next.js and standalone - wiki/payloadcms/authentication-overview — secure cookie settings
Sources
raw/production__deployment.md— https://payloadcms.com/docs/production/deployment