docker.recipes

Directus Headless CMS Stack

intermediate

Open-source headless CMS with PostgreSQL, Redis caching, S3 storage, and full-text search.

Overview

Directus is an open-source headless CMS that transforms any SQL database into a powerful API and elegant admin interface. Founded in 2004 and rebuilt from the ground up in 2018, Directus follows an API-first approach that wraps your database with RESTful and GraphQL endpoints while maintaining complete data portability. Unlike traditional CMSes that lock you into proprietary formats, Directus works directly with your existing database schema. This stack combines Directus with PostgreSQL for robust relational data storage, Redis for high-performance caching and session management, MinIO for S3-compatible object storage, and Meilisearch for lightning-fast search capabilities. Together, these components create a production-ready content management platform that can handle everything from simple blogs to complex enterprise applications with media-rich content and advanced search requirements. This configuration is ideal for developers building modern web applications, mobile apps, or IoT platforms that need flexible content management without the constraints of traditional monolithic CMSes, offering the scalability of microservices with the convenience of integrated tooling.

Key Features

  • Dynamic API generation with RESTful and GraphQL endpoints auto-created from PostgreSQL schema
  • PostgreSQL JSONB support for flexible schema evolution alongside relational data structures
  • Redis-powered session management and query result caching for sub-100ms response times
  • MinIO S3-compatible storage with automatic image transformations and CDN-style asset delivery
  • Meilisearch integration for typo-tolerant full-text search with instant results under 50ms
  • Role-based access control with granular field-level permissions and API key management
  • Real-time collaboration with WebSocket connections for live content editing
  • Database introspection that automatically generates admin interfaces from existing schemas

Common Use Cases

  • 1E-commerce platforms requiring product catalogs with search, media management, and inventory tracking
  • 2Multi-tenant SaaS applications needing per-client content management and API endpoints
  • 3Mobile app backends with user-generated content, file uploads, and push notification management
  • 4Documentation websites with search functionality and collaborative editing workflows
  • 5Digital asset management systems for marketing teams with approval workflows and media transformations
  • 6IoT dashboards collecting sensor data with real-time visualization and historical analysis
  • 7Multi-language websites requiring translation management and localized content delivery

Prerequisites

  • Minimum 4GB RAM (2GB for PostgreSQL + Directus, 512MB each for Redis/MinIO/Meilisearch)
  • Docker Engine 20.10+ with Docker Compose V2 for proper health check and dependency support
  • Available ports 8055 (Directus), 9000-9001 (MinIO), and 7700 (Meilisearch) on your host system
  • Basic understanding of PostgreSQL schema design and SQL queries for data modeling
  • Familiarity with REST/GraphQL APIs and JSON data structures for frontend integration
  • SSL certificate and reverse proxy knowledge for production HTTPS deployment

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 directus:
3 image: directus/directus:latest
4 ports:
5 - "8055:8055"
6 environment:
7 KEY: ${DIRECTUS_KEY}
8 SECRET: ${DIRECTUS_SECRET}
9 DB_CLIENT: pg
10 DB_HOST: postgres
11 DB_PORT: 5432
12 DB_DATABASE: ${POSTGRES_DB}
13 DB_USER: ${POSTGRES_USER}
14 DB_PASSWORD: ${POSTGRES_PASSWORD}
15 CACHE_ENABLED: "true"
16 CACHE_STORE: redis
17 REDIS_HOST: redis
18 REDIS_PORT: 6379
19 STORAGE_LOCATIONS: s3
20 STORAGE_S3_DRIVER: s3
21 STORAGE_S3_KEY: ${MINIO_ACCESS_KEY}
22 STORAGE_S3_SECRET: ${MINIO_SECRET_KEY}
23 STORAGE_S3_BUCKET: directus
24 STORAGE_S3_ENDPOINT: http://minio:9000
25 STORAGE_S3_FORCE_PATH_STYLE: "true"
26 ADMIN_EMAIL: ${ADMIN_EMAIL}
27 ADMIN_PASSWORD: ${ADMIN_PASSWORD}
28 volumes:
29 - directus_uploads:/directus/uploads
30 - directus_extensions:/directus/extensions
31 depends_on:
32 postgres:
33 condition: service_healthy
34 redis:
35 condition: service_started
36 minio:
37 condition: service_started
38 networks:
39 - directus-net
40 restart: unless-stopped
41
42 postgres:
43 image: postgres:16-alpine
44 environment:
45 POSTGRES_USER: ${POSTGRES_USER}
46 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
47 POSTGRES_DB: ${POSTGRES_DB}
48 volumes:
49 - postgres_data:/var/lib/postgresql/data
50 healthcheck:
51 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
52 interval: 10s
53 timeout: 5s
54 retries: 5
55 networks:
56 - directus-net
57 restart: unless-stopped
58
59 redis:
60 image: redis:7-alpine
61 volumes:
62 - redis_data:/data
63 networks:
64 - directus-net
65 restart: unless-stopped
66
67 minio:
68 image: minio/minio:latest
69 ports:
70 - "9000:9000"
71 - "9001:9001"
72 environment:
73 MINIO_ROOT_USER: ${MINIO_ACCESS_KEY}
74 MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY}
75 volumes:
76 - minio_data:/data
77 command: server /data --console-address ":9001"
78 networks:
79 - directus-net
80 restart: unless-stopped
81
82 meilisearch:
83 image: getmeili/meilisearch:latest
84 ports:
85 - "7700:7700"
86 environment:
87 MEILI_MASTER_KEY: ${MEILI_MASTER_KEY}
88 volumes:
89 - meili_data:/meili_data
90 networks:
91 - directus-net
92 restart: unless-stopped
93
94volumes:
95 directus_uploads:
96 directus_extensions:
97 postgres_data:
98 redis_data:
99 minio_data:
100 meili_data:
101
102networks:
103 directus-net:
104 driver: bridge

.env Template

.env
1# Directus Configuration
2DIRECTUS_KEY=random-key-here
3DIRECTUS_SECRET=random-secret-here
4ADMIN_EMAIL=admin@example.com
5ADMIN_PASSWORD=secure_admin_password
6
7# PostgreSQL
8POSTGRES_USER=directus
9POSTGRES_PASSWORD=secure_postgres_password
10POSTGRES_DB=directus
11
12# MinIO
13MINIO_ACCESS_KEY=minioadmin
14MINIO_SECRET_KEY=secure_minio_password
15
16# Meilisearch
17MEILI_MASTER_KEY=secure_meili_master_key

Usage Notes

  1. 1Directus Admin at http://localhost:8055
  2. 2MinIO Console at http://localhost:9001
  3. 3Meilisearch at http://localhost:7700
  4. 4Configure Meilisearch extension in Directus for search

Individual Services(5 services)

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

directus
directus:
  image: directus/directus:latest
  ports:
    - "8055:8055"
  environment:
    KEY: ${DIRECTUS_KEY}
    SECRET: ${DIRECTUS_SECRET}
    DB_CLIENT: pg
    DB_HOST: postgres
    DB_PORT: 5432
    DB_DATABASE: ${POSTGRES_DB}
    DB_USER: ${POSTGRES_USER}
    DB_PASSWORD: ${POSTGRES_PASSWORD}
    CACHE_ENABLED: "true"
    CACHE_STORE: redis
    REDIS_HOST: redis
    REDIS_PORT: 6379
    STORAGE_LOCATIONS: s3
    STORAGE_S3_DRIVER: s3
    STORAGE_S3_KEY: ${MINIO_ACCESS_KEY}
    STORAGE_S3_SECRET: ${MINIO_SECRET_KEY}
    STORAGE_S3_BUCKET: directus
    STORAGE_S3_ENDPOINT: http://minio:9000
    STORAGE_S3_FORCE_PATH_STYLE: "true"
    ADMIN_EMAIL: ${ADMIN_EMAIL}
    ADMIN_PASSWORD: ${ADMIN_PASSWORD}
  volumes:
    - directus_uploads:/directus/uploads
    - directus_extensions:/directus/extensions
  depends_on:
    postgres:
      condition: service_healthy
    redis:
      condition: service_started
    minio:
      condition: service_started
  networks:
    - directus-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:
    - directus-net
  restart: unless-stopped
redis
redis:
  image: redis:7-alpine
  volumes:
    - redis_data:/data
  networks:
    - directus-net
  restart: unless-stopped
minio
minio:
  image: minio/minio:latest
  ports:
    - "9000:9000"
    - "9001:9001"
  environment:
    MINIO_ROOT_USER: ${MINIO_ACCESS_KEY}
    MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY}
  volumes:
    - minio_data:/data
  command: server /data --console-address ":9001"
  networks:
    - directus-net
  restart: unless-stopped
meilisearch
meilisearch:
  image: getmeili/meilisearch:latest
  ports:
    - "7700:7700"
  environment:
    MEILI_MASTER_KEY: ${MEILI_MASTER_KEY}
  volumes:
    - meili_data:/meili_data
  networks:
    - directus-net
  restart: unless-stopped

Quick Start

terminal
1# 1. Create the compose file
2cat > docker-compose.yml << 'EOF'
3services:
4 directus:
5 image: directus/directus:latest
6 ports:
7 - "8055:8055"
8 environment:
9 KEY: ${DIRECTUS_KEY}
10 SECRET: ${DIRECTUS_SECRET}
11 DB_CLIENT: pg
12 DB_HOST: postgres
13 DB_PORT: 5432
14 DB_DATABASE: ${POSTGRES_DB}
15 DB_USER: ${POSTGRES_USER}
16 DB_PASSWORD: ${POSTGRES_PASSWORD}
17 CACHE_ENABLED: "true"
18 CACHE_STORE: redis
19 REDIS_HOST: redis
20 REDIS_PORT: 6379
21 STORAGE_LOCATIONS: s3
22 STORAGE_S3_DRIVER: s3
23 STORAGE_S3_KEY: ${MINIO_ACCESS_KEY}
24 STORAGE_S3_SECRET: ${MINIO_SECRET_KEY}
25 STORAGE_S3_BUCKET: directus
26 STORAGE_S3_ENDPOINT: http://minio:9000
27 STORAGE_S3_FORCE_PATH_STYLE: "true"
28 ADMIN_EMAIL: ${ADMIN_EMAIL}
29 ADMIN_PASSWORD: ${ADMIN_PASSWORD}
30 volumes:
31 - directus_uploads:/directus/uploads
32 - directus_extensions:/directus/extensions
33 depends_on:
34 postgres:
35 condition: service_healthy
36 redis:
37 condition: service_started
38 minio:
39 condition: service_started
40 networks:
41 - directus-net
42 restart: unless-stopped
43
44 postgres:
45 image: postgres:16-alpine
46 environment:
47 POSTGRES_USER: ${POSTGRES_USER}
48 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
49 POSTGRES_DB: ${POSTGRES_DB}
50 volumes:
51 - postgres_data:/var/lib/postgresql/data
52 healthcheck:
53 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
54 interval: 10s
55 timeout: 5s
56 retries: 5
57 networks:
58 - directus-net
59 restart: unless-stopped
60
61 redis:
62 image: redis:7-alpine
63 volumes:
64 - redis_data:/data
65 networks:
66 - directus-net
67 restart: unless-stopped
68
69 minio:
70 image: minio/minio:latest
71 ports:
72 - "9000:9000"
73 - "9001:9001"
74 environment:
75 MINIO_ROOT_USER: ${MINIO_ACCESS_KEY}
76 MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY}
77 volumes:
78 - minio_data:/data
79 command: server /data --console-address ":9001"
80 networks:
81 - directus-net
82 restart: unless-stopped
83
84 meilisearch:
85 image: getmeili/meilisearch:latest
86 ports:
87 - "7700:7700"
88 environment:
89 MEILI_MASTER_KEY: ${MEILI_MASTER_KEY}
90 volumes:
91 - meili_data:/meili_data
92 networks:
93 - directus-net
94 restart: unless-stopped
95
96volumes:
97 directus_uploads:
98 directus_extensions:
99 postgres_data:
100 redis_data:
101 minio_data:
102 meili_data:
103
104networks:
105 directus-net:
106 driver: bridge
107EOF
108
109# 2. Create the .env file
110cat > .env << 'EOF'
111# Directus Configuration
112DIRECTUS_KEY=random-key-here
113DIRECTUS_SECRET=random-secret-here
114ADMIN_EMAIL=admin@example.com
115ADMIN_PASSWORD=secure_admin_password
116
117# PostgreSQL
118POSTGRES_USER=directus
119POSTGRES_PASSWORD=secure_postgres_password
120POSTGRES_DB=directus
121
122# MinIO
123MINIO_ACCESS_KEY=minioadmin
124MINIO_SECRET_KEY=secure_minio_password
125
126# Meilisearch
127MEILI_MASTER_KEY=secure_meili_master_key
128EOF
129
130# 3. Start the services
131docker compose up -d
132
133# 4. View logs
134docker 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/directus-advanced/run | bash

Troubleshooting

  • Directus fails to start with 'Database connection failed': Ensure PostgreSQL health check passes before Directus startup using depends_on conditions
  • File uploads return 500 errors: Verify MinIO bucket 'directus' exists and STORAGE_S3_FORCE_PATH_STYLE is set to true for local MinIO
  • Search queries return empty results: Install and configure Directus Meilisearch extension, then sync your collections to create search indexes
  • Redis connection timeouts during high load: Increase Redis memory limit and enable AOF persistence to prevent data loss during restarts
  • PostgreSQL crashes with 'out of memory': Tune shared_buffers and effective_cache_size in postgresql.conf based on available system RAM
  • MinIO console shows 'InvalidAccessKeyId': Ensure MINIO_ROOT_USER matches STORAGE_S3_KEY and both are at least 3 characters long

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