Production Deployment

There are many ways to deploy Payload to a production environment. When evaluating how you will deploy Payload, you need to consider these main aspects:

  1. Basics
  2. Security
  3. Your database
  4. Permanent File Storage
  5. Docker

Payload can be deployed anywhere that Next.js can run - including Vercel, Netlify, SST, DigitalOcean, AWS, and more. Because it's open source, you can self-host it.

But it's important to remember that most Payload projects will also need a database, file storage, an email provider, and a CDN. Make sure you have all of the requirements that your project needs, no matter what deployment platform you choose.

Often, the easiest and fastest way to deploy Payload is to use Payload Cloud — where you get everything you need out of the box, including:

  1. A MongoDB Atlas database
  2. S3 file storage
  3. Resend email service
  4. Cloudflare CDN
  5. Blue / green deployments
  6. Logs
  7. And more

Basics

Payload runs fully in Next.js, so the Next.js build process is used for building Payload. If you've used create-payload-app to create your project, executing the build npm script will build Payload for production.

Security

Payload features a suite of security features that you can rely on to strengthen your application's security. When deploying to Production, it's a good idea to double-check that you are making proper use of each of them.

The Secret Key

When you initialize Payload, you provide it with a secret property. This property should be impossible to guess and extremely difficult for brute-force attacks to crack. Make sure your Production secret is a long, complex string.

Double-check and thoroughly test all Access Control

Because you are in complete control of who can do what with your data, you should double and triple-check that you wield that power responsibly before deploying to Production.

Running in Production

Depending on where you deploy Payload, you may need to provide a start script to your deployment platform in order to start up Payload in production mode.

Note that this is different than running next dev. Generally, Next.js apps come configured with a start script which runs next start.

Secure Cookie Settings

You should be using an SSL certificate for production Payload instances, which means you can enable secure cookies in your Authentication-enabled Collection configs.

Preventing API Abuse

Payload comes with a robust set of built-in anti-abuse measures, such as locking out users after X amount of failed login attempts, GraphQL query complexity limits, max depth settings, and more. Click here to learn more.

Database

Payload can be used with any Postgres database or MongoDB-compatible database including AWS DocumentDB or Azure Cosmos DB. Make sure your production environment has access to the database that Payload uses.

Out of the box, Payload templates pass the process.env.DATABASE_URI environment variable to its database adapters, so make sure you've got that environment variable (and all others that you use) assigned in your deployment platform.

DocumentDB

When using AWS DocumentDB, you will need to configure connection options for authentication in the connectOptions passed to the mongooseAdapter . You also need to set connectOptions.useFacet to false to disable use of the unsupported $facet aggregation.

CosmosDB

When using Azure Cosmos DB, an index is needed for any field you may want to sort on. To add the sort index for all fields that may be sorted in the admin UI use the indexSortableFields configuration option.

File storage

If you are using Payload to manage file uploads, you need to consider where your uploaded files will be permanently stored. If you do not use Payload for file uploads, then this section does not impact your app whatsoever.

Persistent vs Ephemeral Filesystems

Some cloud app hosts such as Heroku use ephemeral file systems, which means that any files uploaded to your server only last until the server restarts or shuts down. Heroku and similar providers schedule restarts and shutdowns without your control, meaning your uploads will accidentally disappear without any way to get them back.

Alternatively, persistent filesystems will never delete your files and can be trusted to reliably host uploads perpetually.

Popular cloud providers with ephemeral filesystems:

  • Heroku
  • DigitalOcean Apps

Popular cloud providers with persistent filesystems:

  • DigitalOcean Droplets
  • Amazon EC2
  • GoDaddy
  • Many other more traditional web hosts

Using cloud storage providers

If you don't use Payload's upload functionality, you can completely disregard this section.

But, if you do, and you still want to use an ephemeral filesystem provider, you can use one of Payload's official cloud storage plugins or write your own to save the files your users upload to a more permanent storage solution like Amazon S3 or DigitalOcean Spaces.

Payload provides a list of official cloud storage adapters for you to use:

Follow the docs to configure any one of these storage providers. For local development, it might be handy to simply store uploads on your own computer, and then when it comes to production, simply enable the plugin for the cloud storage vendor of your choice.

Docker

This is an example of a multi-stage docker build of Payload for production. Ensure you are setting your environment variables on deployment, like PAYLOAD_SECRET, PAYLOAD_CONFIG_PATH, and DATABASE_URI if needed.

1
# From https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile
2
3
FROM node:18-alpine AS base
4
5
# Install dependencies only when needed
6
FROM base AS deps
7
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
8
RUN apk add --no-cache libc6-compat
9
WORKDIR /app
10
11
# Install dependencies based on the preferred package manager
12
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
13
RUN \
14
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
15
elif [ -f package-lock.json ]; then npm ci; \
16
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
17
else echo "Lockfile not found." && exit 1; \
18
fi
19
20
21
# Rebuild the source code only when needed
22
FROM base AS builder
23
WORKDIR /app
24
COPY --from=deps /app/node_modules ./node_modules
25
COPY . .
26
27
# Next.js collects completely anonymous telemetry data about general usage.
28
# Learn more here: https://nextjs.org/telemetry
29
# Uncomment the following line in case you want to disable telemetry during the build.
30
# ENV NEXT_TELEMETRY_DISABLED 1
31
32
RUN \
33
if [ -f yarn.lock ]; then yarn run build; \
34
elif [ -f package-lock.json ]; then npm run build; \
35
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
36
else echo "Lockfile not found." && exit 1; \
37
fi
38
39
# Production image, copy all the files and run next
40
FROM base AS runner
41
WORKDIR /app
42
43
ENV NODE_ENV production
44
# Uncomment the following line in case you want to disable telemetry during runtime.
45
# ENV NEXT_TELEMETRY_DISABLED 1
46
47
RUN addgroup --system --gid 1001 nodejs
48
RUN adduser --system --uid 1001 nextjs
49
50
COPY --from=builder /app/public ./public
51
52
# Set the correct permission for prerender cache
53
RUN mkdir .next
54
RUN chown nextjs:nodejs .next
55
56
# Automatically leverage output traces to reduce image size
57
# https://nextjs.org/docs/advanced-features/output-file-tracing
58
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
59
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
60
61
USER nextjs
62
63
EXPOSE 3000
64
65
ENV PORT 3000
66
67
# server.js is created by next build from the standalone output
68
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
69
CMD HOSTNAME="0.0.0.0" node server.js

Docker Compose

Here is an example of a docker-compose.yml file that can be used for development

1
version: '3'
2
3
services:
4
payload:
5
image: node:18-alpine
6
ports:
7
- '3000:3000'
8
volumes:
9
- .:/home/node/app
10
- node_modules:/home/node/app/node_modules
11
working_dir: /home/node/app/
12
command: sh -c "corepack enable && corepack prepare pnpm@latest --activate && pnpm install && pnpm dev"
13
depends_on:
14
- mongo
15
# - postgres
16
env_file:
17
- .env
18
19
# Ensure your DATABASE_URI uses 'mongo' as the hostname ie. mongodb://mongo/my-db-name
20
mongo:
21
image: mongo:latest
22
ports:
23
- '27017:27017'
24
command:
25
- --storageEngine=wiredTiger
26
volumes:
27
- data:/data/db
28
logging:
29
driver: none
30
31
# Uncomment the following to use postgres
32
# postgres:
33
# restart: always
34
# image: postgres:latest
35
# volumes:
36
# - pgdata:/var/lib/postgresql/data
37
# ports:
38
# - "5432:5432"
39
40
volumes:
41
data:
42
# pgdata:
43
node_modules:
Next

Preventing Production API Abuse