docker.recipes

Strapi + Next.js Full Stack

intermediate

Headless CMS with Strapi backend and Next.js frontend.

Overview

Strapi is an open-source headless Content Management System (CMS) that provides a flexible, developer-first approach to content management. Built entirely in JavaScript, Strapi separates content management from presentation, offering both RESTful and GraphQL APIs out of the box. This architecture allows developers to manage content through Strapi's intuitive admin panel while delivering that content to any frontend framework or platform. This stack combines Strapi's powerful content management capabilities with Next.js, React's production-ready framework that excels at both static site generation and server-side rendering. PostgreSQL serves as the robust relational database for Strapi's content storage, while Redis provides high-performance caching and session management. NGINX acts as the reverse proxy, handling SSL termination, load balancing, and routing between the frontend and backend services. Together, these components create a modern JAMstack architecture that can scale from startup MVPs to enterprise-grade applications. Development teams building content-heavy applications, marketing websites, e-commerce platforms, or multi-channel digital experiences will find this stack particularly valuable. The headless approach means content creators can work independently in Strapi while developers have complete freedom to build custom frontends in Next.js. This separation enables faster development cycles, better performance through static generation, and the flexibility to deliver content to web, mobile, and IoT applications from a single source.

Key Features

  • Visual content-type builder in Strapi for creating custom data models without code
  • Next.js static site generation (SSG) and server-side rendering (SSR) for optimal performance
  • RESTful and GraphQL APIs automatically generated from Strapi content models
  • Role-based access control with customizable permissions for content editors
  • PostgreSQL JSONB support for flexible content structures alongside relational data
  • Redis-powered session storage and API response caching
  • Strapi's plugin ecosystem for extending functionality (SEO, internationalization, media)
  • Next.js incremental static regeneration for updating static content without full rebuilds

Common Use Cases

  • 1Marketing websites requiring frequent content updates by non-technical team members
  • 2E-commerce platforms needing product catalogs with complex attributes and relationships
  • 3Multi-brand companies delivering content to multiple websites from a centralized CMS
  • 4SaaS applications requiring user-generated content management and admin dashboards
  • 5News and publishing platforms with editorial workflows and content scheduling
  • 6Mobile app backends providing structured content through Strapi's REST/GraphQL APIs
  • 7International websites leveraging Strapi's i18n plugin for multi-language content management

Prerequisites

  • Docker and Docker Compose installed with at least 3GB available RAM
  • Ports 80, 443, 1337, and 3000 available on the host system
  • Basic understanding of React/Next.js development for frontend customization
  • Familiarity with REST APIs or GraphQL for connecting frontend to Strapi backend
  • Node.js development environment for building custom Strapi plugins if needed
  • SSL certificates for production deployment with NGINX HTTPS configuration

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 strapi:
3 image: strapi/strapi:latest
4 ports:
5 - "1337:1337"
6 environment:
7 DATABASE_CLIENT: postgres
8 DATABASE_HOST: postgres
9 DATABASE_PORT: 5432
10 DATABASE_NAME: ${POSTGRES_DB}
11 DATABASE_USERNAME: ${POSTGRES_USER}
12 DATABASE_PASSWORD: ${POSTGRES_PASSWORD}
13 JWT_SECRET: ${JWT_SECRET}
14 ADMIN_JWT_SECRET: ${ADMIN_JWT_SECRET}
15 APP_KEYS: ${APP_KEYS}
16 volumes:
17 - strapi_app:/srv/app
18 depends_on:
19 postgres:
20 condition: service_healthy
21 networks:
22 - strapi-net
23 restart: unless-stopped
24
25 nextjs:
26 build:
27 context: ./frontend
28 dockerfile: Dockerfile
29 ports:
30 - "3000:3000"
31 environment:
32 STRAPI_URL: http://strapi:1337
33 depends_on:
34 - strapi
35 networks:
36 - strapi-net
37 restart: unless-stopped
38
39 postgres:
40 image: postgres:16-alpine
41 environment:
42 POSTGRES_USER: ${POSTGRES_USER}
43 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
44 POSTGRES_DB: ${POSTGRES_DB}
45 volumes:
46 - postgres_data:/var/lib/postgresql/data
47 healthcheck:
48 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
49 interval: 10s
50 timeout: 5s
51 retries: 5
52 networks:
53 - strapi-net
54 restart: unless-stopped
55
56 redis:
57 image: redis:7-alpine
58 volumes:
59 - redis_data:/data
60 networks:
61 - strapi-net
62 restart: unless-stopped
63
64 nginx:
65 image: nginx:alpine
66 ports:
67 - "80:80"
68 - "443:443"
69 volumes:
70 - ./nginx.conf:/etc/nginx/nginx.conf:ro
71 depends_on:
72 - strapi
73 - nextjs
74 networks:
75 - strapi-net
76 restart: unless-stopped
77
78volumes:
79 strapi_app:
80 postgres_data:
81 redis_data:
82
83networks:
84 strapi-net:
85 driver: bridge

.env Template

.env
1# PostgreSQL
2POSTGRES_USER=strapi
3POSTGRES_PASSWORD=secure_postgres_password
4POSTGRES_DB=strapi
5
6# Strapi Secrets
7JWT_SECRET=$(openssl rand -base64 32)
8ADMIN_JWT_SECRET=$(openssl rand -base64 32)
9APP_KEYS=$(openssl rand -base64 32),$(openssl rand -base64 32)

Usage Notes

  1. 1Strapi Admin at http://localhost:1337/admin
  2. 2Next.js frontend at http://localhost:3000
  3. 3Create Dockerfile for Next.js frontend
  4. 4Configure nginx.conf for production routing

Individual Services(5 services)

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

strapi
strapi:
  image: strapi/strapi:latest
  ports:
    - "1337:1337"
  environment:
    DATABASE_CLIENT: postgres
    DATABASE_HOST: postgres
    DATABASE_PORT: 5432
    DATABASE_NAME: ${POSTGRES_DB}
    DATABASE_USERNAME: ${POSTGRES_USER}
    DATABASE_PASSWORD: ${POSTGRES_PASSWORD}
    JWT_SECRET: ${JWT_SECRET}
    ADMIN_JWT_SECRET: ${ADMIN_JWT_SECRET}
    APP_KEYS: ${APP_KEYS}
  volumes:
    - strapi_app:/srv/app
  depends_on:
    postgres:
      condition: service_healthy
  networks:
    - strapi-net
  restart: unless-stopped
nextjs
nextjs:
  build:
    context: ./frontend
    dockerfile: Dockerfile
  ports:
    - "3000:3000"
  environment:
    STRAPI_URL: http://strapi:1337
  depends_on:
    - strapi
  networks:
    - strapi-net
  restart: unless-stopped
postgres
postgres:
  image: postgres:16-alpine
  environment:
    POSTGRES_USER: ${POSTGRES_USER}
    POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    POSTGRES_DB: ${POSTGRES_DB}
  volumes:
    - postgres_data:/var/lib/postgresql/data
  healthcheck:
    test:
      - CMD-SHELL
      - pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}
    interval: 10s
    timeout: 5s
    retries: 5
  networks:
    - strapi-net
  restart: unless-stopped
redis
redis:
  image: redis:7-alpine
  volumes:
    - redis_data:/data
  networks:
    - strapi-net
  restart: unless-stopped
nginx
nginx:
  image: nginx:alpine
  ports:
    - "80:80"
    - "443:443"
  volumes:
    - ./nginx.conf:/etc/nginx/nginx.conf:ro
  depends_on:
    - strapi
    - nextjs
  networks:
    - strapi-net
  restart: unless-stopped

Quick Start

terminal
1# 1. Create the compose file
2cat > docker-compose.yml << 'EOF'
3services:
4 strapi:
5 image: strapi/strapi:latest
6 ports:
7 - "1337:1337"
8 environment:
9 DATABASE_CLIENT: postgres
10 DATABASE_HOST: postgres
11 DATABASE_PORT: 5432
12 DATABASE_NAME: ${POSTGRES_DB}
13 DATABASE_USERNAME: ${POSTGRES_USER}
14 DATABASE_PASSWORD: ${POSTGRES_PASSWORD}
15 JWT_SECRET: ${JWT_SECRET}
16 ADMIN_JWT_SECRET: ${ADMIN_JWT_SECRET}
17 APP_KEYS: ${APP_KEYS}
18 volumes:
19 - strapi_app:/srv/app
20 depends_on:
21 postgres:
22 condition: service_healthy
23 networks:
24 - strapi-net
25 restart: unless-stopped
26
27 nextjs:
28 build:
29 context: ./frontend
30 dockerfile: Dockerfile
31 ports:
32 - "3000:3000"
33 environment:
34 STRAPI_URL: http://strapi:1337
35 depends_on:
36 - strapi
37 networks:
38 - strapi-net
39 restart: unless-stopped
40
41 postgres:
42 image: postgres:16-alpine
43 environment:
44 POSTGRES_USER: ${POSTGRES_USER}
45 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
46 POSTGRES_DB: ${POSTGRES_DB}
47 volumes:
48 - postgres_data:/var/lib/postgresql/data
49 healthcheck:
50 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
51 interval: 10s
52 timeout: 5s
53 retries: 5
54 networks:
55 - strapi-net
56 restart: unless-stopped
57
58 redis:
59 image: redis:7-alpine
60 volumes:
61 - redis_data:/data
62 networks:
63 - strapi-net
64 restart: unless-stopped
65
66 nginx:
67 image: nginx:alpine
68 ports:
69 - "80:80"
70 - "443:443"
71 volumes:
72 - ./nginx.conf:/etc/nginx/nginx.conf:ro
73 depends_on:
74 - strapi
75 - nextjs
76 networks:
77 - strapi-net
78 restart: unless-stopped
79
80volumes:
81 strapi_app:
82 postgres_data:
83 redis_data:
84
85networks:
86 strapi-net:
87 driver: bridge
88EOF
89
90# 2. Create the .env file
91cat > .env << 'EOF'
92# PostgreSQL
93POSTGRES_USER=strapi
94POSTGRES_PASSWORD=secure_postgres_password
95POSTGRES_DB=strapi
96
97# Strapi Secrets
98JWT_SECRET=$(openssl rand -base64 32)
99ADMIN_JWT_SECRET=$(openssl rand -base64 32)
100APP_KEYS=$(openssl rand -base64 32),$(openssl rand -base64 32)
101EOF
102
103# 3. Start the services
104docker compose up -d
105
106# 4. View logs
107docker 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/strapi-nextjs-stack/run | bash

Troubleshooting

  • Strapi 'Database connection refused': Ensure PostgreSQL health check passes before Strapi starts using depends_on condition
  • Next.js build failing with 'STRAPI_URL not found': Verify environment variable points to http://strapi:1337 for container-to-container communication
  • NGINX 502 Bad Gateway: Check that both Strapi (port 1337) and Next.js (port 3000) containers are running and responsive
  • Strapi admin panel shows 'Application configuration error': Generate proper JWT_SECRET, ADMIN_JWT_SECRET, and APP_KEYS in environment variables
  • PostgreSQL data loss after container restart: Ensure postgres_data volume is properly mounted to /var/lib/postgresql/data
  • Redis connection timeout in Strapi: Configure Strapi's Redis session store to use redis:6379 as the connection string

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