Payload CMS Headless
Payload CMS headless with MongoDB.
Overview
Payload CMS is a modern, TypeScript-first headless content management system that prioritizes developer experience with a code-first approach. Built on Node.js, Payload generates admin interfaces automatically from configuration files and provides powerful APIs for content delivery, making it an ideal choice for developers who want full control over their CMS architecture without sacrificing usability for content editors.
This deployment creates a production-ready Payload CMS environment with three specialized services: the main Payload application running on Node.js 20 Alpine, a dedicated MongoDB 6 database instance for content storage, and an NGINX reverse proxy for efficient request handling and static file serving. The configuration mounts the Payload application code directly into the container, enabling rapid development cycles while maintaining persistent data storage for both media files and database content.
This stack is perfect for development teams building headless websites, mobile applications, or multi-channel digital experiences who need a CMS that can scale with their TypeScript codebase. The combination of Payload's developer-friendly architecture with MongoDB's flexible document storage and NGINX's high-performance web serving creates an efficient foundation for content-driven applications that require both technical flexibility and editorial ease of use.
Key Features
- TypeScript-first CMS with automatic admin UI generation from configuration files
- MongoDB document-based storage with flexible schema evolution for content types
- Built-in media management with persistent volume storage for uploaded assets
- NGINX reverse proxy configuration for optimized static file delivery and caching
- Hot-reload development environment with direct code mounting for rapid iteration
- RESTful and GraphQL APIs automatically generated from Payload field configurations
- Environment-based configuration supporting multiple deployment stages
- Containerized architecture enabling consistent behavior across development environments
Common Use Cases
- 1Headless e-commerce platforms requiring flexible product catalog management with TypeScript integration
- 2Multi-site content management for agencies serving multiple clients from a single CMS instance
- 3Mobile app backends needing structured content delivery with real-time API access
- 4Developer portfolio sites and blogs where technical control over CMS architecture is essential
- 5Startup MVPs requiring rapid content modeling iteration without database migration complexity
- 6Enterprise marketing websites needing both developer flexibility and content editor accessibility
- 7Documentation platforms and knowledge bases with complex content relationships and categorization
Prerequisites
- Docker and Docker Compose installed with at least 3GB available RAM for optimal MongoDB performance
- Node.js 20 project structure with Payload CMS already initialized in the ./app directory
- PAYLOAD_SECRET environment variable configured with a secure random string for session management
- Basic TypeScript knowledge for customizing Payload configuration and field schemas
- NGINX configuration file prepared at ./nginx.conf with appropriate proxy settings for port 3000
- Port availability: 3000 for Payload CMS and 80 for NGINX (customizable via environment variables)
For development & testing. Review security settings, change default credentials, and test thoroughly before production use. See Terms
docker-compose.yml
docker-compose.yml
1services: 2 payload: 3 image: node:20-alpine4 container_name: payload-cms5 restart: unless-stopped6 working_dir: /app7 command: npm start8 ports: 9 - "${PAYLOAD_PORT:-3000}:3000"10 environment: 11 - MONGODB_URI=mongodb://payload-mongo:27017/payload12 - PAYLOAD_SECRET=${PAYLOAD_SECRET}13 volumes: 14 - ./app:/app15 - payload_media:/app/media16 depends_on: 17 - payload-mongo1819 payload-mongo: 20 image: mongo:621 container_name: payload-mongo22 restart: unless-stopped23 volumes: 24 - mongo_data:/data/db2526 nginx: 27 image: nginx:alpine28 container_name: payload-nginx29 restart: unless-stopped30 ports: 31 - "${NGINX_PORT:-80}:80"32 volumes: 33 - ./nginx.conf:/etc/nginx/nginx.conf:ro3435volumes: 36 payload_media: 37 mongo_data: .env Template
.env
1# Payload CMS2PAYLOAD_PORT=30003PAYLOAD_SECRET=your_payload_secret4NGINX_PORT=80Usage Notes
- 1Payload at http://localhost:3000
- 2Admin at /admin
- 3Create payload project first
- 4TypeScript-first CMS
Individual Services(3 services)
Copy individual services to mix and match with your existing compose files.
payload
payload:
image: node:20-alpine
container_name: payload-cms
restart: unless-stopped
working_dir: /app
command: npm start
ports:
- ${PAYLOAD_PORT:-3000}:3000
environment:
- MONGODB_URI=mongodb://payload-mongo:27017/payload
- PAYLOAD_SECRET=${PAYLOAD_SECRET}
volumes:
- ./app:/app
- payload_media:/app/media
depends_on:
- payload-mongo
payload-mongo
payload-mongo:
image: mongo:6
container_name: payload-mongo
restart: unless-stopped
volumes:
- mongo_data:/data/db
nginx
nginx:
image: nginx:alpine
container_name: payload-nginx
restart: unless-stopped
ports:
- ${NGINX_PORT:-80}:80
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
Quick Start
terminal
1# 1. Create the compose file2cat > docker-compose.yml << 'EOF'3services:4 payload:5 image: node:20-alpine6 container_name: payload-cms7 restart: unless-stopped8 working_dir: /app9 command: npm start10 ports:11 - "${PAYLOAD_PORT:-3000}:3000"12 environment:13 - MONGODB_URI=mongodb://payload-mongo:27017/payload14 - PAYLOAD_SECRET=${PAYLOAD_SECRET}15 volumes:16 - ./app:/app17 - payload_media:/app/media18 depends_on:19 - payload-mongo2021 payload-mongo:22 image: mongo:623 container_name: payload-mongo24 restart: unless-stopped25 volumes:26 - mongo_data:/data/db2728 nginx:29 image: nginx:alpine30 container_name: payload-nginx31 restart: unless-stopped32 ports:33 - "${NGINX_PORT:-80}:80"34 volumes:35 - ./nginx.conf:/etc/nginx/nginx.conf:ro3637volumes:38 payload_media:39 mongo_data:40EOF4142# 2. Create the .env file43cat > .env << 'EOF'44# Payload CMS45PAYLOAD_PORT=300046PAYLOAD_SECRET=your_payload_secret47NGINX_PORT=8048EOF4950# 3. Start the services51docker compose up -d5253# 4. View logs54docker compose logs -fOne-Liner
Run this command to download and set up the recipe in one step:
terminal
1curl -fsSL https://docker.recipes/api/recipes/payload-cms-stack/run | bashTroubleshooting
- MongoDB connection refused error: Verify payload-mongo container is running and accessible on port 27017 within Docker network
- Payload fails to start with 'PAYLOAD_SECRET required': Set PAYLOAD_SECRET environment variable with minimum 32-character random string
- NGINX 502 Bad Gateway errors: Check that Payload container is healthy on port 3000 and NGINX upstream configuration matches service name
- File permission errors on media uploads: Ensure payload_media volume has correct write permissions for Node.js user in Alpine container
- TypeScript compilation errors on startup: Verify all dependencies are installed in ./app directory and tsconfig.json is properly configured
- Admin interface not accessible: Confirm Payload is configured with proper server URL and admin settings in payload.config.ts
Community Notes
Loading...
Loading notes...
Download Recipe Kit
Get all files in a ready-to-deploy package
Includes docker-compose.yml, .env template, README, and license
Ad Space
Shortcuts: C CopyF FavoriteD Download