Self-Host Payload CMS: Modern TypeScript Headless CMS & Application Framework
What is Payload CMS?
Payload is a revolutionary TypeScript-based headless CMS and application framework that accelerates how developers build modern digital experiences. Unlike traditional CMS platforms, Payload provides everything you need in one place - content management, authentication, file storage, and APIs - without the complexity of microservices. This self-hosted alternative to Strapi, Contentful, and Sanity offers enterprise-grade features with complete data ownership and no vendor lock-in.
Key Features of Payload Platform
🚀 Developer-First Architecture
- 100% TypeScript: Type-safe from database to API with full IntelliSense support
- Code-First Configuration: Define your content model in code, not through UI clicks
- Express.js Foundation: Built on familiar Node.js technologies you already know
- React Admin Panel: Fully customizable admin UI with React components
- GraphQL & REST APIs: Auto-generated, fully documented APIs from your schema
🎨 Powerful Admin Interface
- Beautiful Admin UI: Modern, responsive interface that clients and editors love
- Live Preview: Real-time content preview with your frontend application
- Block-Based Editor: Flexible content blocks for rich page building
- Localization Built-in: Multi-language content management out of the box
- Media Management: Advanced file upload with automatic image optimization
🔐 Enterprise Features
- Authentication System: Complete auth with users, roles, and permissions
- Access Control: Fine-grained, field-level access control
- Drafts & Versioning: Content versioning with draft/publish workflows
- Webhooks: Real-time integrations with external systems
- Search: Full-text search powered by your database
⚡ Performance & Scalability
- Database Agnostic: Works with MongoDB and PostgreSQL
- Automatic Migrations: Schema changes handled automatically
- Query Optimization: Efficient queries with relationship population
- Caching Strategy: Built-in caching for optimal performance
- Horizontal Scaling: Deploy across multiple servers seamlessly
Why Choose Payload Over Commercial Alternatives?
Payload vs Strapi (Open Source)
Feature | Payload | Strapi |
---|---|---|
TypeScript Native | ✅ Full TypeScript | ⚠️ JavaScript with TS definitions |
Admin Customization | ✅ React Components | ❌ Limited customization |
Code-First Config | ✅ Yes | ❌ UI-based configuration |
Field-Level Access | ✅ Built-in | ❌ Basic RBAC only |
Block Editor | ✅ Native support | ❌ Requires plugins |
Live Preview | ✅ Built-in | ❌ Limited support |
Payload vs Contentful ($489+/month)
Feature | Payload (Self-Hosted) | Contentful |
---|---|---|
Monthly Cost | Free & Open Source | $489-2000+/month |
Data Ownership | ✅ Complete Control | ❌ Vendor Lock-in |
API Rate Limits | ✅ Unlimited | ❌ Rate Limited |
Custom Fields | ✅ Unlimited | ❌ Plan Limitations |
Environments | ✅ Unlimited | ❌ Limited by Plan |
Storage | ✅ Your Infrastructure | ❌ Quota Limits |
Payload vs Sanity ($99+/month)
- Hosting Flexibility: Self-host anywhere vs cloud-only deployment
- Database Choice: MongoDB or PostgreSQL vs proprietary database
- Query Language: GraphQL/REST vs GROQ learning curve
- Pricing Model: Free forever vs usage-based pricing
- Data Portability: Standard database vs proprietary format
Quick Deployment Options
Option 1: Docker Compose (Recommended)
Perfect for production deployments with complete control.
version: '3.8'
services:
payload:
image: node:18-alpine
working_dir: /app
ports:
- "3000:3000"
environment:
- DATABASE_URI=mongodb://mongo:27017/payload
- PAYLOAD_SECRET=your-secret-key-here
- NODE_ENV=production
command: npx create-payload-app
depends_on:
- mongo
volumes:
- ./media:/app/media
- ./src:/app/src
mongo:
image: mongo:6
ports:
- "27017:27017"
volumes:
- mongo_data:/data/db
environment:
- MONGO_INITDB_DATABASE=payload
volumes:
mongo_data:
Option 2: Create Payload App (Quick Start)
Fastest way to get started with Payload CMS.
# Create new Payload project
npx create-payload-app@latest my-project
# Choose your options:
# - Database: MongoDB or PostgreSQL
# - Template: Blank, Blog, E-commerce, or Custom
# - TypeScript: Yes (recommended)
cd my-project
npm run dev
# Access at http://localhost:3000/admin
Option 3: Manual Installation
For adding Payload to existing Express applications.
# Install Payload and dependencies
npm install payload express
npm install -D typescript @types/express
# Create payload.config.ts
cat > payload.config.ts << 'EOF'
import { buildConfig } from 'payload/config';
import Users from './collections/Users';
import Media from './collections/Media';
export default buildConfig({
serverURL: 'http://localhost:3000',
admin: {
user: Users.slug,
},
collections: [
Users,
Media,
// Add your collections here
],
typescript: {
outputFile: './types/payload-types.ts',
},
});
EOF
# Initialize and start
npm run dev
Getting Started with Payload CMS
Initial Setup Process
- Install Payload: Use create-payload-app or add to existing project
- Configure Database: Set up MongoDB or PostgreSQL connection
- Create First User: Access /admin to create administrator account
- Define Collections: Create your content models in TypeScript
- Customize Admin: Extend the admin UI with React components
Collection Configuration Example
// collections/Articles.ts
import { CollectionConfig } from 'payload/types';
const Articles: CollectionConfig = {
slug: 'articles',
admin: {
useAsTitle: 'title',
defaultColumns: ['title', 'status', 'createdAt'],
},
access: {
read: () => true,
create: ({ req: { user } }) => Boolean(user),
update: ({ req: { user } }) => Boolean(user?.roles?.includes('editor')),
},
fields: [
{
name: 'title',
type: 'text',
required: true,
localized: true,
},
{
name: 'content',
type: 'richText',
required: true,
},
{
name: 'author',
type: 'relationship',
relationTo: 'users',
required: true,
},
{
name: 'status',
type: 'select',
options: ['draft', 'published'],
defaultValue: 'draft',
},
{
name: 'publishedAt',
type: 'date',
admin: {
position: 'sidebar',
},
},
],
hooks: {
beforeChange: [
({ data, req }) => {
// Auto-set published date
if (data.status === 'published' && !data.publishedAt) {
data.publishedAt = new Date();
}
return data;
},
],
},
};
export default Articles;
API Usage Examples
// REST API Examples
// GET all articles
fetch('http://localhost:3000/api/articles')
// GET single article
fetch('http://localhost:3000/api/articles/123')
// CREATE article (authenticated)
fetch('http://localhost:3000/api/articles', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_TOKEN'
},
body: JSON.stringify({
title: 'New Article',
content: 'Article content...',
author: 'user-id'
})
})
// GraphQL Query Example
const query = `
query GetArticles {
Articles(limit: 10, where: { status: { equals: "published" } }) {
docs {
id
title
content
author {
name
email
}
publishedAt
}
totalDocs
}
}
`;
Popular Use Cases
Content Management System
- Marketing Websites: Build dynamic marketing sites with page builder
- Multi-site Management: Manage multiple websites from one CMS
- Blog Platform: Full-featured blogging with categories and authors
- Documentation Sites: Technical documentation with versioning
E-commerce Platform
- Product Catalog: Complex product management with variants
- Order Management: Complete order processing workflow
- Customer Portal: User accounts with order history
- Inventory Tracking: Stock management and notifications
Application Backend
- SaaS Applications: Complete backend for software products
- Mobile App APIs: Backend for React Native/Flutter apps
- Internal Tools: Admin panels and dashboards
- Workflow Automation: Business process management
Digital Asset Management
- Media Library: Centralized media storage and distribution
- Image Optimization: Automatic resizing and format conversion
- Video Management: Stream video content with thumbnails
- Document Repository: Secure document storage and sharing
Advanced Features & Customization
Custom React Components
// Custom field component
import React from 'react';
import { useField } from 'payload/components/forms';
const ColorPicker: React.FC = () => {
const { value, setValue } = useField();
return (
<input
type="color"
value={value || '#000000'}
onChange={(e) => setValue(e.target.value)}
/>
);
};
// Register in field config
{
name: 'color',
type: 'text',
admin: {
components: {
Field: ColorPicker,
},
},
}
Authentication & Access Control
// Advanced access control
access: {
read: ({ req: { user } }) => {
if (user?.roles?.includes('admin')) return true;
return {
author: {
equals: user?.id,
},
};
},
update: ({ req: { user }, id }) => {
// Custom logic for update permissions
return checkUserPermission(user, id);
},
}
Hooks & Middleware
// Collection hooks
hooks: {
beforeChange: [
async ({ data, req, operation }) => {
if (operation === 'create') {
// Send notification
await sendEmail({
to: 'admin@example.com',
subject: 'New content created',
body: `New ${data.title} created`,
});
}
return data;
},
],
afterRead: [
async ({ doc, req }) => {
// Enhance document with computed fields
doc.readingTime = calculateReadingTime(doc.content);
return doc;
},
],
}
Payload CMS Community & Ecosystem
- GitHub Stars: 20,000+ stars with rapid growth
- Discord Community: Active community with 5,000+ developers
- Plugin Ecosystem: Growing library of community plugins
- Documentation: Comprehensive docs with examples and tutorials
- Enterprise Support: Commercial support and consulting available
- Regular Updates: Monthly releases with new features
Migration Guide
From WordPress
- Export Content: Use WordPress export tools or direct database access
- Create Collections: Map WordPress post types to Payload collections
- Import Script: Write migration script using Payload's Local API
- Media Migration: Transfer wp-content/uploads to Payload media
- User Migration: Import WordPress users with role mapping
From Strapi
- Schema Mapping: Convert Strapi content types to Payload collections
- API Migration: Update frontend to use Payload's REST/GraphQL APIs
- Plugin Replacement: Find Payload equivalents for Strapi plugins
- Database Migration: Export from Strapi and import to Payload
- Custom Code: Port any custom Strapi code to Payload hooks
Transform your content management with Payload CMS - the modern, TypeScript-native CMS that gives you complete control without the complexity of microservices or expensive SaaS subscriptions.