Application Development AWS

Payload CMS on AWS User Guide

| Product: Payload CMS on AWS

Overview

This image runs Payload CMS, the open source, code first, TypeScript headless content management system built natively on Next.js. Payload lets teams define content models in code, manage entries through a polished admin panel, and deliver everything over a fully typed REST and GraphQL API.

The Payload application is installed under /opt/payload and runs as a dedicated unprivileged payload system account under a systemd service that starts it on boot and restarts it on failure. The production Next.js bundle is pre built into the image, so a production grade content platform is running within minutes of launch. The content store, holding the SQLite database (payload.db) and your uploaded media, lives at /var/lib/payload, which is a dedicated, independently resizable EBS data volume.

Payload binds to the loopback interface only (127.0.0.1:3000) and is never exposed directly. An nginx reverse proxy publishes the admin panel and the content API on port 80. Payload provides its own admin authentication at /admin, so the nginx proxy is a plain pass through and adds no second auth layer. The admin password is generated on the first boot of every deployed instance, so two instances launched from the same Amazon Machine Image never share a password. It is written to /root/payload-cms-credentials.txt with mode 0600 so that only the root user can read it.

Prerequisites

Before you deploy this image you need:

  • An Amazon Web Services account where you can launch EC2 instances
  • IAM permissions to launch instances, create security groups, and subscribe to AWS Marketplace products
  • An EC2 key pair in the target Region for SSH access to the instance
  • A VPC and subnet in the target Region, with a security group allowing inbound port 22 from your management network and port 80 for the admin panel and the content API
  • The AWS CLI (version 2) installed locally if you plan to deploy from the command line

Step 1: Launch the Instance from the AWS Marketplace

Sign in to the AWS Management Console, open the EC2 service, and select Launch instance. Under Application and OS Images choose AWS Marketplace AMIs and search for Payload CMS. Select the cloudimg listing and choose Select, then Continue on the subscription summary.

Pick an instance type of t3.large or larger. Choose your EC2 key pair under Key pair (login). Under Network settings select your VPC and subnet, and either create or select a security group that opens port 22 from your management network and port 80 for the admin panel and the API. Leave the root volume at the default size or larger.

Select Launch instance. First boot initialisation takes a short time after the instance state becomes Running and the status checks pass: it generates the application secret and the admin user, then starts the service.

Step 2: Launch the Instance from the AWS CLI

The following block launches an instance from the cloudimg Payload CMS Marketplace AMI into an existing subnet and security group. Replace <ami-id> with the AMI ID shown on the Marketplace listing, <key-name> with your EC2 key pair name, <subnet-id> with your subnet ID, and <security-group-id> with a security group that opens ports 22 and 80 as described above.

aws ec2 run-instances \
  --image-id <ami-id> \
  --instance-type t3.large \
  --key-name <key-name> \
  --subnet-id <subnet-id> \
  --security-group-ids <security-group-id> \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=payload-cms}]'

When the instance reaches the Running state and its status checks pass, note its public IP address or DNS name from the EC2 console or with aws ec2 describe-instances.

Step 3: Connect to Your Instance

Connect over SSH using your key pair and the login user for your operating system variant.

OS variant SSH login user
Ubuntu 24.04 ubuntu
ssh -i <key-name>.pem ubuntu@<public-ip>

Step 4: Retrieve the Admin Password

The admin password is unique to your instance and was generated on first boot. Read it as root:

sudo cat /root/payload-cms-credentials.txt

The file lists the admin panel URL, the admin email (admin@cloudimg.local) and the generated password. Keep this password somewhere safe.

Step 5: Sign In to the Admin Panel

The Payload admin panel is served on port 80 by nginx at the /admin path. In a browser, go to:

http://<instance-public-ip>/admin

Sign in with the email admin@cloudimg.local and the password from the credentials file. After signing in you land on the dashboard, which lists the collections defined in your Payload config.

The Payload admin dashboard listing the collections

Open a collection from the sidebar or the dashboard to see its list view, where you browse, search, filter and manage entries. Every collection you define in code is automatically given a list view, an editor, access control and a typed API.

The Payload admin collection list view

Selecting an entry opens the field based document editor, where you edit the fields defined for that collection. The same editor powers your own collections once you add them to the Payload config.

The Payload admin document editor

Step 6: Confirm Payload Is Running

Over SSH, confirm the Payload service and the nginx proxy are active and that the ports are listening:

sudo systemctl is-active payload nginx
sudo ss -tlnp | grep -E ':(80|3000) '

You should see both services reported as active, Payload listening on 127.0.0.1:3000 (loopback only), and nginx listening on port 80.

Step 7: Use the Content API

Payload exposes a fully typed REST and GraphQL content API on the same port 80, under the /api path. Obtain a session token by posting your admin credentials to the login endpoint. From the instance you can call the API over loopback:

PASS=$(sudo grep '^payload.admin.pass=' /root/payload-cms-credentials.txt | cut -d= -f2-)
curl -s -H 'Content-Type: application/json' \
  --data "{\"email\":\"admin@cloudimg.local\",\"password\":\"${PASS}\"}" \
  http://127.0.0.1/api/users/login | head -c 400; echo

The response contains a session token and the authenticated user. From outside the instance, send the same request to http://<instance-public-ip>/api/users/login. List the documents in a collection with a GET to /api/<collection-slug>, for example the users collection:

curl -s http://127.0.0.1/api/users?limit=1 | head -c 400; echo

The GraphQL endpoint is available at /api/graphql and the GraphQL Playground at /api/graphql-playground. Generate API keys and manage access control from the admin panel.

Step 8: Define Your Own Collections

Payload is code first: you define collections and globals in TypeScript in /opt/payload/src and Payload generates the admin panel, the typed API and the database schema for you. The configuration entry point is /opt/payload/src/payload.config.ts and the collection definitions live under /opt/payload/src/collections. View the current config:

sudo sed -n '1,40p' /opt/payload/src/payload.config.ts

After changing the config or adding a collection, generate a new database migration and rebuild the production bundle as the payload user, then restart the service. SQLite requires migrations to apply schema changes in production:

cd /opt/payload
sudo -u payload env HOME=/opt/payload NODE_ENV=production npx payload migrate:create my_change
sudo -u payload env HOME=/opt/payload NODE_ENV=production npx payload migrate
sudo -u payload env HOME=/opt/payload NODE_ENV=production npm run build
sudo systemctl restart payload

Consult the Payload documentation at https://payloadcms.com/docs for the full configuration, field type and access control reference.

Step 9: The Data Volume

The content store lives on a dedicated EBS volume mounted at /var/lib/payload. This keeps the SQLite database and your uploaded media off the operating system disk and lets you resize or snapshot it independently. Confirm the mount and the database location with:

df -h /var/lib/payload
sudo ls -la /var/lib/payload

You will see payload.db and the media directory on the mounted volume. To grow the content store, expand the EBS volume in the AWS console, then grow the filesystem on the instance with sudo resize2fs on the underlying device.

Step 10: Enable HTTPS

The admin panel and API are served over plain HTTP on port 80 by nginx. For production use, place them behind TLS. Obtain a certificate for your domain (for example with a managed certificate on an Application Load Balancer in front of the instance, or with Certbot installed on the instance), then configure nginx to listen on 443 with your certificate and proxy to 127.0.0.1:3000 exactly as the bundled site does for port 80. Restrict the security group so ports 80 and 443 are reachable only from the networks that need the admin panel and the API. When you serve Payload over a public domain, set the serverURL in your Payload config to the HTTPS URL and rebuild.

Step 11: Backup and Maintenance

Back up the content store by snapshotting the /var/lib/payload EBS volume, which captures the SQLite database and all uploaded media. You can also copy the database file off the instance for an application level backup, for example sudo cp /var/lib/payload/payload.db /path/to/backup/payload.db.

Apply operating system security updates with sudo apt-get update && sudo apt-get upgrade and reboot when a new kernel is installed; Payload and nginx start automatically on boot.

Support

This image is published and supported by cloudimg. Support covers deployment, content modelling, the REST and GraphQL API, access control, media and upload configuration, database and storage, TLS and runtime tuning. Contact cloudimg through the support channel listed on the AWS Marketplace listing.

All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.