Deploy Payload CMS: 3 Paths & Real Bills
Three deployment paths for Payload CMS—Vercel + managed Postgres, a Docker VPS with local disk, and managed DB + S3—each with real low‑traffic bills, media handling, preview, and database configuration detailed step by step.

Knowing how to deploy Payload CMS is the last mile that turns your headless CMS into a live application—and the hosting shape you pick directly dictates your architecture, monthly bill, and operational chores. Because Payload rides on Next.js, you can drop it anywhere Next.js runs (Vercel, a Docker VPS, or even SST on AWS). But that choice forces decisions about the database, file storage, preview URLs, and your migration queue. This guide walks three concrete shapes, what they cost at low traffic, and the config that makes each one hum.
If you want to skip the ops entirely, we handle Payload deployments for startups and studios—learn more about our web services.
How to Deploy Payload CMS: Three Deployment Shapes
Each path below is a real, production‑grade pattern. We’ll detail the setup, the actual bills you’ll pay for a site that serves a few thousand visitors a month, and where the sharp edges hide.
Path 1: Vercel + Managed Postgres (serverless front‑to‑back)
Takeaway: The fastest path to production, but your bill is tied to database and media storage, not compute.
Vercel’s Hobby plan hosts the Next.js app for free. You pair it with a managed Postgres—Neon, Supabase, or Vercel Postgres—and offload file uploads to an S3‑compatible service (Vercel Blob, Cloudflare R2, or AWS S3). The serverless functions that serve Payload’s REST/GraphQL API stay well under the Hobby execution limits for low traffic.
Steps
- Clone the template. Start from the official Payload + Vercel starter. It wires up the Postgres adapter and includes example collections.
- Provision the database. Create a project in Neon (free tier: 0.5 GB, 1‑branch). Paste the connection string into
DATABASE_URIin your Vercel environment. - Plug in the cloud storage adapter. Payload needs a place to persist uploads. Install the official S3 adapter:
npm install @payloadcms/plugin-cloud-storage @payloadcms/plugin-cloud-storage-s3
In payload.config.ts, activate it and point it at your bucket:
import { cloudStorage } from '@payloadcms/plugin-cloud-storage'
import { s3Adapter } from '@payloadcms/plugin-cloud-storage-s3'
export default buildConfig({
plugins: [
cloudStorage({
collections: {
media: {
adapter: s3Adapter({
config: {
endpoint: process.env.S3_ENDPOINT,
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY!,
secretAccessKey: process.env.S3_SECRET!,
},
region: 'auto',
},
bucket: process.env.S3_BUCKET!,
}),
},
},
}),
],
})
(We’ve detailed this plugin and others we always install in our Payload plugins breakdown.)
- Set the preview URL. In your Payload config, point
serverURLto your Vercel deployment domain. The Live Preview will work as long as the Next.js front‑end is served from the same origin.
Bills at low traffic
Resource | Provider | Cost |
|---|---|---|
Next.js hosting | Vercel Hobby | Free |
Database | Neon Free Tier | Free (0.5 GB, 1 branch) |
Object storage | Cloudflare R2 | Free (10 GB) |
Total |
| $0 |
When you outgrow the free tiers, bumping Neon to 1 GB ($19/mo) and R2 to $0.015/GB (or moving to Vercel Blob at $0.30/GB stored) keeps the bill under $25/mo. The serverless database cold‑starts are imperceptible for content teams, and you never touch a server.
Path 2: Docker on a VPS with local disk (self‑contained everything)
Takeaway: A single‑instance VPS gives you a predictable $6‑12/mo bill and full control—you trade away zero‑downtime scaling for absolute simplicity.
This is our go‑to when a client wants a fixed, predictable cost and doesn’t need global replication. You run Payload inside a Docker container on a small VPS (DigitalOcean Droplet, Hetzner CX22, etc.), with a companion Postgres container and a mounted volume for uploads.
Steps
- Write the Dockerfile. A lightweight multi‑stage build that outputs the Next.js standalone server:
FROM node:20-alpine AS base
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=base /app/.next/standalone ./
COPY --from=base /app/.next/static ./.next/static
COPY --from=base /app/public ./public
EXPOSE 3000
CMD ["node", "server.js"]
- Docker Compose with Postgres and a volume.
services:
app:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URI=postgresql://payload:secret@db:5432/payload
- PAYLOAD_SECRET=your‑secret
- UPLOAD_DIR=/media
volumes:
- media:/media
depends_on:
- db
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: payload
POSTGRES_PASSWORD: secret
POSTGRES_DB: payload
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
media:
pgdata:
- Configure Payload for local disk. No cloud‑storage plugin required—Payload’s default
uploadcollection writes to theUPLOAD_DIR. Inpayload.config.ts:
upload: {
staticDir: process.env.UPLOAD_DIR || 'media',
},
- Automated backups. A cron job on the host zips the
pgdataandmediavolumes nightly and syncs them to an S3‑compatible bucket usingrclone. A simple script:
#!/bin/bash
TIMESTAMP=$(date +%F)
docker run --rm -v payload_pgdata:/pgdata -v payload_media:/media alpine \
tar czf /backup/payload-backup-$TIMESTAMP.tar.gz /pgdata /media
rclone copy /backup/payload-backup-$TIMESTAMP.tar.gz s3:mybackupbucket/
- Preview. The
serverURLpoints tohttp://your‑domain.com. Since the VPS serves both the admin panel and the front‑end from the same origin, live preview works out of the box.
Bills at low traffic
Resource | Provider | Cost |
|---|---|---|
VPS (1 vCPU, 2 GB) | DigitalOcean Droplet | $6/mo |
Object storage | DigitalOcean Spaces | $5/mo (250 GB) |
Total |
| $11/mo |
The 2 GB RAM handles Payload’s admin with a few concurrent editors; if you start running webhooks or migrations queues heavily, bump to 4 GB ($12/mo). Everything—database, app, and media—lives on one machine, so you avoid network latency between services.
Path 3: Managed Postgres + S3‑compatible storage (decoupled database, anywhere hosting)
Takeaway: You get a highly‑available database and durable media storage that survive any app‑layer host change; you can front it with Vercel Hobby, a cheap VPS, or even Cloudflare Workers.
This design separates concerns. The database (Neon, Supabase, Aiven, etc.) stays up even if you tear down the app server. Media lands in an S3‑compatible bucket (R2, Spaces, Backblaze B2). The app container can run on any platform—Vercel Hobby, Fly.io, Railway, or a $4‑per‑month VPS.
Steps
- Provision the database. Grab a connection string from Neon or Supabase. Set
DATABASE_URI. - Set up S3 and the adapter. Install the same S3 plugin as in Path 1. Use your bucket provider’s endpoint.
- Host the app. You can deploy via Vercel (free) using a standard
@payloadcms/nextbuild, or push a Docker image to Fly.io (free for 256 MB RAM, which is enough for small Payload apps). A minimalfly.toml:
app = "my-payload-app"
kill_signal = "SIGINT"
kill_timeout = 5
[build]
image = "node:20-alpine"
command = "npm run build"
[[services]]
internal_port = 3000
- Migrations queue. Because the database is external, you can run your Payload migrations locally and then push them to the managed DB without worrying about the app container. The
payload migrateCLI works directly against the remoteDATABASE_URI.
Bills at low traffic (Vercel Hobby + Neon + R2)
Resource | Provider | Cost |
|---|---|---|
App hosting | Vercel Hobby | Free |
Database | Neon Free Tier | Free |
Object storage | Cloudflare R2 | Free (10 GB) |
Total |
| $0 |
Even if you later bump the database to a 1‑GB plan ($19/mo) and add a backup region ($10/mo), you’re still under $30/mo with zero server administration. Because the app tier is disposable, you can switch hosting providers without migrating any data—a benefit we’ve leaned on when a client’s traffic pattern changed.
FAQ
Which deployment method is cheapest for Payload CMS?
Usually a VPS running Docker with a local Postgres container and local disk for media costs a flat $6‑12/month with no egress fees. Managed Postgres + S3 starts around $5‑9/month (DB free tier + object storage) if fronted by Vercel Hobby. The VPS route is cheapest at rest but requires managing backups yourself.
Can I use the Vercel free tier for Payload CMS production?
Yes, and many small sites do. Vercel Hobby gives 1‑second timeout on Serverless Functions, which is enough for Payload’s API. You’ll still need an external Postgres (Neon free tier works) and, ideally, an S3‑compatible bucket for uploads so media survives re‑deploys.
How do you handle media uploads in Payload without cloud storage?
Bind the container’s /media folder to a Docker volume. Pair that with a nightly cron that zips the volume and syncs it to a cheap object store (or your workstation) using rclone. For redundancy, you can mount the same volume to an offsite server, but a periodic pull backup is usually adequate for low‑traffic sites.