docker.recipes

KeystoneJS CMS

intermediate

KeystoneJS headless CMS with PostgreSQL.

Overview

KeystoneJS is a modern headless CMS and GraphQL API framework built on Node.js that enables developers to create powerful content management systems with a type-safe development experience. Originally developed by Thinkmill, KeystoneJS provides an intuitive admin UI, automatic GraphQL API generation, and flexible schema definition capabilities, making it ideal for building everything from simple websites to complex multi-tenant applications. This deployment creates a complete KeystoneJS CMS environment with three dedicated services: a KeystoneJS application container running on Node.js 20 Alpine, a PostgreSQL 15 database for robust data storage, and an NGINX reverse proxy for production-ready web serving. The KeystoneJS container connects to the PostgreSQL database using environment variables, while NGINX handles incoming requests and can be configured for SSL termination, caching, and load balancing. This stack is perfect for development teams who need a headless CMS with strong typing, automatic API generation, and the reliability of PostgreSQL for data persistence. The combination provides excellent performance for content-heavy applications while maintaining the flexibility to serve multiple frontend applications through KeystoneJS's GraphQL endpoint at /api/graphql.

Key Features

  • Automatic GraphQL API generation with type-safe schema definitions
  • Built-in Admin UI for content management accessible through web interface
  • PostgreSQL 15 integration with ACID compliance and advanced querying capabilities
  • Session-based authentication with configurable session secrets
  • NGINX reverse proxy configuration for production deployment scenarios
  • Hot-reload development support through volume mounting of application code
  • Persistent data storage with dedicated PostgreSQL volume management
  • Environment-based configuration for database connections and security settings

Common Use Cases

  • 1Headless CMS for React, Vue, or Next.js frontend applications
  • 2Multi-site content management with shared backend infrastructure
  • 3E-commerce product catalog management with GraphQL API integration
  • 4Blog and publishing platforms requiring structured content workflows
  • 5Corporate websites needing non-technical content editor interfaces
  • 6API-first applications requiring rapid content model prototyping
  • 7Development environments for KeystoneJS application testing and staging

Prerequisites

  • Docker and Docker Compose with minimum 2GB available RAM for all services
  • Node.js knowledge for KeystoneJS schema configuration and customization
  • Port 3000 and 80 available on host system for KeystoneJS and NGINX
  • Basic understanding of GraphQL queries and mutations for API interaction
  • PostgreSQL familiarity for database maintenance and performance tuning
  • Existing KeystoneJS application code in ./app directory before container startup

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 keystone:
3 image: node:20-alpine
4 container_name: keystone-cms
5 restart: unless-stopped
6 working_dir: /app
7 command: npm run start
8 ports:
9 - "${KEYSTONE_PORT:-3000}:3000"
10 environment:
11 - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@keystone-db/keystone
12 - SESSION_SECRET=${SESSION_SECRET}
13 volumes:
14 - ./app:/app
15 depends_on:
16 - keystone-db
17
18 keystone-db:
19 image: postgres:15-alpine
20 container_name: keystone-db
21 restart: unless-stopped
22 environment:
23 - POSTGRES_USER=${DB_USER}
24 - POSTGRES_PASSWORD=${DB_PASSWORD}
25 - POSTGRES_DB=keystone
26 volumes:
27 - keystone_db_data:/var/lib/postgresql/data
28
29 nginx:
30 image: nginx:alpine
31 container_name: keystone-nginx
32 restart: unless-stopped
33 ports:
34 - "${NGINX_PORT:-80}:80"
35 volumes:
36 - ./nginx.conf:/etc/nginx/nginx.conf:ro
37
38volumes:
39 keystone_db_data:

.env Template

.env
1# KeystoneJS
2KEYSTONE_PORT=3000
3DB_USER=keystone
4DB_PASSWORD=keystone_password
5SESSION_SECRET=your_session_secret
6NGINX_PORT=80

Usage Notes

  1. 1Keystone at http://localhost:3000
  2. 2GraphQL at /api/graphql
  3. 3Create keystone project first
  4. 4Define schema in keystone.ts

Individual Services(3 services)

Copy individual services to mix and match with your existing compose files.

keystone
keystone:
  image: node:20-alpine
  container_name: keystone-cms
  restart: unless-stopped
  working_dir: /app
  command: npm run start
  ports:
    - ${KEYSTONE_PORT:-3000}:3000
  environment:
    - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@keystone-db/keystone
    - SESSION_SECRET=${SESSION_SECRET}
  volumes:
    - ./app:/app
  depends_on:
    - keystone-db
keystone-db
keystone-db:
  image: postgres:15-alpine
  container_name: keystone-db
  restart: unless-stopped
  environment:
    - POSTGRES_USER=${DB_USER}
    - POSTGRES_PASSWORD=${DB_PASSWORD}
    - POSTGRES_DB=keystone
  volumes:
    - keystone_db_data:/var/lib/postgresql/data
nginx
nginx:
  image: nginx:alpine
  container_name: keystone-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 file
2cat > docker-compose.yml << 'EOF'
3services:
4 keystone:
5 image: node:20-alpine
6 container_name: keystone-cms
7 restart: unless-stopped
8 working_dir: /app
9 command: npm run start
10 ports:
11 - "${KEYSTONE_PORT:-3000}:3000"
12 environment:
13 - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@keystone-db/keystone
14 - SESSION_SECRET=${SESSION_SECRET}
15 volumes:
16 - ./app:/app
17 depends_on:
18 - keystone-db
19
20 keystone-db:
21 image: postgres:15-alpine
22 container_name: keystone-db
23 restart: unless-stopped
24 environment:
25 - POSTGRES_USER=${DB_USER}
26 - POSTGRES_PASSWORD=${DB_PASSWORD}
27 - POSTGRES_DB=keystone
28 volumes:
29 - keystone_db_data:/var/lib/postgresql/data
30
31 nginx:
32 image: nginx:alpine
33 container_name: keystone-nginx
34 restart: unless-stopped
35 ports:
36 - "${NGINX_PORT:-80}:80"
37 volumes:
38 - ./nginx.conf:/etc/nginx/nginx.conf:ro
39
40volumes:
41 keystone_db_data:
42EOF
43
44# 2. Create the .env file
45cat > .env << 'EOF'
46# KeystoneJS
47KEYSTONE_PORT=3000
48DB_USER=keystone
49DB_PASSWORD=keystone_password
50SESSION_SECRET=your_session_secret
51NGINX_PORT=80
52EOF
53
54# 3. Start the services
55docker compose up -d
56
57# 4. View logs
58docker compose logs -f

One-Liner

Run this command to download and set up the recipe in one step:

terminal
1curl -fsSL https://docker.recipes/api/recipes/keystone-cms-stack/run | bash

Troubleshooting

  • keystone-cms container exits immediately: Ensure ./app directory contains valid package.json with start script and KeystoneJS dependencies installed
  • Database connection refused errors: Verify keystone-db container is running and DATABASE_URL environment variables match PostgreSQL credentials
  • NGINX 502 Bad Gateway responses: Check that keystone container is accessible on port 3000 and update nginx.conf upstream configuration
  • GraphQL endpoint returns 404 errors: Confirm KeystoneJS application started successfully and schema is properly defined in keystone.ts configuration file
  • Session authentication not working: Verify SESSION_SECRET environment variable is set and has sufficient entropy for security
  • PostgreSQL data not persisting: Ensure keystone_db_data volume is properly mounted and PostgreSQL container has write permissions

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