KeystoneJS CMS
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-alpine4 container_name: keystone-cms5 restart: unless-stopped6 working_dir: /app7 command: npm run start8 ports: 9 - "${KEYSTONE_PORT:-3000}:3000"10 environment: 11 - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@keystone-db/keystone12 - SESSION_SECRET=${SESSION_SECRET}13 volumes: 14 - ./app:/app15 depends_on: 16 - keystone-db1718 keystone-db: 19 image: postgres:15-alpine20 container_name: keystone-db21 restart: unless-stopped22 environment: 23 - POSTGRES_USER=${DB_USER}24 - POSTGRES_PASSWORD=${DB_PASSWORD}25 - POSTGRES_DB=keystone26 volumes: 27 - keystone_db_data:/var/lib/postgresql/data2829 nginx: 30 image: nginx:alpine31 container_name: keystone-nginx32 restart: unless-stopped33 ports: 34 - "${NGINX_PORT:-80}:80"35 volumes: 36 - ./nginx.conf:/etc/nginx/nginx.conf:ro3738volumes: 39 keystone_db_data: .env Template
.env
1# KeystoneJS2KEYSTONE_PORT=30003DB_USER=keystone4DB_PASSWORD=keystone_password5SESSION_SECRET=your_session_secret6NGINX_PORT=80Usage Notes
- 1Keystone at http://localhost:3000
- 2GraphQL at /api/graphql
- 3Create keystone project first
- 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 file2cat > docker-compose.yml << 'EOF'3services:4 keystone:5 image: node:20-alpine6 container_name: keystone-cms7 restart: unless-stopped8 working_dir: /app9 command: npm run start10 ports:11 - "${KEYSTONE_PORT:-3000}:3000"12 environment:13 - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@keystone-db/keystone14 - SESSION_SECRET=${SESSION_SECRET}15 volumes:16 - ./app:/app17 depends_on:18 - keystone-db1920 keystone-db:21 image: postgres:15-alpine22 container_name: keystone-db23 restart: unless-stopped24 environment:25 - POSTGRES_USER=${DB_USER}26 - POSTGRES_PASSWORD=${DB_PASSWORD}27 - POSTGRES_DB=keystone28 volumes:29 - keystone_db_data:/var/lib/postgresql/data3031 nginx:32 image: nginx:alpine33 container_name: keystone-nginx34 restart: unless-stopped35 ports:36 - "${NGINX_PORT:-80}:80"37 volumes:38 - ./nginx.conf:/etc/nginx/nginx.conf:ro3940volumes:41 keystone_db_data:42EOF4344# 2. Create the .env file45cat > .env << 'EOF'46# KeystoneJS47KEYSTONE_PORT=300048DB_USER=keystone49DB_PASSWORD=keystone_password50SESSION_SECRET=your_session_secret51NGINX_PORT=8052EOF5354# 3. Start the services55docker compose up -d5657# 4. View logs58docker 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/keystone-cms-stack/run | bashTroubleshooting
- 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
Shortcuts: C CopyF FavoriteD Download