Skip to content

Deploying Payload CMS

Introduction

Payload CMS is a self-hosted, open-source headless CMS and application framework built with TypeScript and React. Unlike traditional CMS platforms, Payload gives developers complete control through code-first configuration while providing editors with a powerful, intuitive admin interface.

Designed for modern web development, Payload combines the flexibility of a headless CMS with the capabilities of an application framework, making it ideal for everything from simple blogs to complex enterprise applications.

Key highlights of Payload CMS:

  • Code-First Configuration: Define your schema in TypeScript for type safety and version control
  • Powerful Admin UI: Auto-generated admin panel based on your configuration
  • GraphQL and REST APIs: Automatic API generation for all collections
  • Authentication: Built-in authentication with access control at every level
  • File Uploads: Media management with image resizing and cloud storage support
  • Localization: First-class support for multilingual content
  • Draft System: Save drafts and preview content before publishing
  • Versions and Revisions: Track changes and restore previous versions
  • Blocks and Rich Text: Flexible content modeling with reusable blocks
  • Hooks and Plugins: Extend functionality with lifecycle hooks and plugins
  • Self-Hosted: Deploy anywhere with complete data ownership
  • Open Source: Licensed under MIT

This guide walks through deploying Payload CMS on Klutch.sh using Docker, configuring the application, and setting up for production use.

Why Deploy Payload CMS on Klutch.sh

Deploying Payload CMS on Klutch.sh provides several advantages:

Simplified Deployment: Klutch.sh automatically detects your Dockerfile and builds Payload without complex orchestration. Push to GitHub, and your CMS deploys automatically.

Persistent Storage: Attach persistent volumes for your database and media uploads. Your content survives container restarts and redeployments.

HTTPS by Default: Klutch.sh provides automatic SSL certificates for secure admin and API access.

GitHub Integration: Connect your Payload configuration repository directly. Updates trigger automatic redeployments.

Environment Variable Management: Securely store database credentials and API keys through Klutch.sh’s environment variable system.

Custom Domains: Use your organization’s domain for a professional CMS experience.

Prerequisites

Before deploying Payload CMS on Klutch.sh, ensure you have:

  • A Klutch.sh account
  • A GitHub account with a repository for your Payload project
  • Basic familiarity with Docker, Node.js, and TypeScript
  • A MongoDB database (can be deployed on Klutch.sh or use MongoDB Atlas)

Preparing Your Repository

Create a GitHub repository containing your Payload CMS project.

Repository Structure

payload-project/
├── Dockerfile
├── package.json
├── tsconfig.json
├── src/
│ ├── payload.config.ts
│ ├── server.ts
│ └── collections/
│ ├── Users.ts
│ ├── Pages.ts
│ └── Media.ts
└── .dockerignore

Creating the Payload Configuration

Create src/payload.config.ts:

import { buildConfig } from 'payload/config';
import path from 'path';
import Users from './collections/Users';
import Pages from './collections/Pages';
import Media from './collections/Media';
export default buildConfig({
serverURL: process.env.PAYLOAD_PUBLIC_SERVER_URL || 'http://localhost:3000',
admin: {
user: Users.slug,
},
collections: [
Users,
Pages,
Media,
],
upload: {
limits: {
fileSize: 5000000, // 5MB
},
},
typescript: {
outputFile: path.resolve(__dirname, 'payload-types.ts'),
},
graphQL: {
schemaOutputFile: path.resolve(__dirname, 'generated-schema.graphql'),
},
});

Creating the Dockerfile

Create a Dockerfile in the root of your repository:

FROM node:18-alpine AS builder
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci
# Copy source code
COPY . .
# Build the application
RUN npm run build
# Production stage
FROM node:18-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install production dependencies only
RUN npm ci --only=production
# Copy built application
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/build ./build
# Create media directory
RUN mkdir -p /app/media
# Set environment variables
ENV NODE_ENV=production
ENV PAYLOAD_CONFIG_PATH=dist/payload.config.js
# Expose the application port
EXPOSE 3000
# Start the application
CMD ["node", "dist/server.js"]

Creating the .dockerignore File

Create a .dockerignore file:

.git
.github
*.md
LICENSE
.gitignore
*.log
.DS_Store
node_modules/
.env
.env.local
dist/
build/

Environment Variables Reference

VariableRequiredDescription
MONGODB_URIYesMongoDB connection string
PAYLOAD_SECRETYesSecret key for encryption and signing
PAYLOAD_PUBLIC_SERVER_URLYesPublic URL of your Payload instance
PAYLOAD_PORTNoServer port (default: 3000)

Deploying Payload CMS on Klutch.sh

    Generate a Payload Secret

    Generate a secure secret key:

    Terminal window
    openssl rand -hex 32

    Set Up MongoDB

    Deploy a MongoDB instance on Klutch.sh or use MongoDB Atlas. Note the connection string.

    Push Your Repository to GitHub

    Initialize your repository and push to GitHub with your Payload project.

    Create a New Project on Klutch.sh

    Navigate to the Klutch.sh dashboard and create a new project. Give it a descriptive name like “payload-cms” or “content-backend”.

    Create a New App

    Within your project, create a new app. Connect your GitHub account if you haven’t already, then select the repository containing your Payload project.

    Configure HTTP Traffic

    In the deployment settings:

    • Select HTTP as the traffic type
    • Set the internal port to 3000

    Set Environment Variables

    Add the following environment variables:

    VariableValue
    MONGODB_URIYour MongoDB connection string
    PAYLOAD_SECRETYour generated secret key
    PAYLOAD_PUBLIC_SERVER_URLhttps://your-app-name.klutch.sh

    Attach Persistent Volumes

    Add the following volumes:

    Mount PathRecommended SizePurpose
    /app/media10+ GBMedia uploads and files

    Deploy Your Application

    Click Deploy to start the build process. Klutch.sh will:

    • Detect your Dockerfile automatically
    • Build the container image
    • Attach the persistent volumes
    • Start the Payload container
    • Provision an HTTPS certificate

    Access Payload CMS

    Once deployment completes, access your Payload admin at https://your-app-name.klutch.sh/admin. Create your first admin user on the initial visit.

Initial Setup

Creating the First User

  1. Navigate to https://your-app-name.klutch.sh/admin
  2. Complete the registration form
  3. Your first user becomes an admin automatically

Defining Collections

Collections are the core building blocks in Payload:

src/collections/Pages.ts
import { CollectionConfig } from 'payload/types';
const Pages: CollectionConfig = {
slug: 'pages',
admin: {
useAsTitle: 'title',
},
access: {
read: () => true,
},
fields: [
{
name: 'title',
type: 'text',
required: true,
},
{
name: 'content',
type: 'richText',
},
{
name: 'status',
type: 'select',
options: [
{ label: 'Draft', value: 'draft' },
{ label: 'Published', value: 'published' },
],
defaultValue: 'draft',
},
],
};
export default Pages;

Media Collection

src/collections/Media.ts
import { CollectionConfig } from 'payload/types';
const Media: CollectionConfig = {
slug: 'media',
upload: {
staticDir: 'media',
imageSizes: [
{ name: 'thumbnail', width: 400, height: 300, position: 'centre' },
{ name: 'card', width: 768, height: 1024, position: 'centre' },
],
mimeTypes: ['image/*', 'application/pdf'],
},
fields: [
{
name: 'alt',
type: 'text',
},
],
};
export default Media;

Using the API

REST API

Payload automatically generates REST endpoints:

Terminal window
# Get all pages
curl "https://your-payload-instance/api/pages"
# Get a specific page
curl "https://your-payload-instance/api/pages/{id}"
# Create a page (authenticated)
curl -X POST "https://your-payload-instance/api/pages" \
-H "Authorization: JWT your-token" \
-H "Content-Type: application/json" \
-d '{"title": "New Page", "content": [...]}'

GraphQL API

Access GraphQL at /api/graphql:

query {
Pages {
docs {
id
title
content
status
}
}
}

Advanced Features

Access Control

Define granular access control:

access: {
read: () => true, // Public read
create: ({ req: { user } }) => Boolean(user), // Authenticated only
update: ({ req: { user } }) => user?.role === 'admin',
delete: ({ req: { user } }) => user?.role === 'admin',
}

Hooks

Add lifecycle hooks:

hooks: {
beforeChange: [
({ data }) => {
data.updatedAt = new Date();
return data;
},
],
afterChange: [
async ({ doc }) => {
// Trigger webhook, update cache, etc.
},
],
}

Plugins

Extend Payload with plugins for features like SEO, redirects, and more.

Additional Resources

Conclusion

Deploying Payload CMS on Klutch.sh gives you a powerful, self-hosted headless CMS with the flexibility of an application framework. The combination of code-first configuration and auto-generated admin UI means you get both developer experience and editor usability.

With TypeScript throughout, automatic API generation, and extensive customization options, Payload provides the foundation for building modern content-driven applications while keeping your data under your control.