Directus: Headless CMS and Data Platform
Directus is a flexible backend for all your projects that turns any SQL database into a headless CMS, admin panel, or app with custom UI, instant APIs, authentication, and more.
- Step 1
What is Directus?
Directus is a powerful, fast, and flexible headless CMS and data platform that wraps around any SQL database. It provides instant APIs, real-time data, authentication, role-based permissions, and a customizable admin panel for managing your database content.
Unlike traditional CMS platforms that come with their own database schema, Directus works directly with your existing database structure. This means you maintain full control over your data model while gaining immediate access to powerful CMS features.
Key Features:
- Instant REST and GraphQL APIs from existing database schema
- No vendor lock-in - your database remains untouched and portable
- Authentication and role-based access control (RBAC)
- Real-time data via WebSockets
- Custom UI through Vue.js app dashboard
- File management with multiple storage drivers
- Flow-based automation for data processing
- Extensions system for custom functionality
- Multi-language support and internationalization
- AI integration for content generation
Core Concepts:
- Collections: Maps to database tables
- Fields: Column types with validation and relationships
- Items: Individual row records in collections
- Users & Roles: Authentication and permission management
- Flows: Visual automation builder
- Assets: On-the-fly image transformations
- Permissions: Granular access control system
- Step 2
Technology stack and architecture
Directus is built on TypeScript with a modern, modular architecture supporting multiple databases and features through its extension system.
Core Technology Stack:
- Runtime: Node.js 22+ (ES modules)
- Language: TypeScript (compiled to ES modules)
- Package Manager: pnpm 10+
- Build Tool: tsdown for bundling, vite for frontend
Backend API (Directus API):
- Express.js for HTTP server and routing
- Knex.js as SQL query builder and migrations
- WebSocket server for real-time updates (ws, graphql-ws)
- Redis/ioredis for caching and message queue
- Pino for structured logging
- Argon2 for password hashing
- Sharp for image processing
- TUS protocol for resumable uploads
Frontend App (Directus App):
- Vue.js 3 with Composition API
- Vite build tool
- Pinia for state management
- Vue Router for SPA navigation
- Vue I18n for localization
Database Support:
- PostgreSQL (via pg)
- MySQL/MariaDB (via mysql2)
- SQLite (via sqlite3)
- SQL Server/MSSQL (via tedious)
- CockroachDB (PostgreSQL-compatible)
- Oracle (via oracledb)
Storage Integrations:
- Local filesystem
- AWS S3 and S3-compatible
- Azure Blob Storage
- Google Cloud Storage
- Cloudinary CDN
- Supabase Storage
Authentication Providers:
- Email/Password (default)
- OAuth2/OIDC (Google, GitHub, etc.)
- SAML 2.0
- LDAP/Active Directory
- API Keys
AI Integration:
- AI SDK for LLM interactions
- OpenAI, Anthropic, Google AI support
- MCP (Model Context Protocol) for AI agent workflows
Directus Architecture Overview: +------------------+ API/UI +------------------+ | Web Browser | ──►│ Directus API | | (Admin App) |◄─────│ (Node.js) | +------------------+ +------------------+ │ +-----------------+-----------------+ │ │ │ +-----▼----+ +-----▼----+ +-----▼----+ | Cache | | Queue | | Storage | | (Redis) | | (Redis) | | Files | +----------+ +----------+ +----------+ │ │ │ +-----------------+-----------------+ │ +-----------------+-----------------+ │ │ │ +-----▼──────+ +-------▼------+ +-------▼------+ | PostgreSQL | | MySQL | | SQLite | | MariaDB | | CockroachDB | | SQL Server | +------------+ +--------------+ +-------------+ - Step 3
Installation methods
Directus offers multiple installation methods to suit different environments and preferences.
Docker (Recommended for most users): docker run -p 8050:8050 -v $PWD/directus-data:/directus/data directus/directus
Docker Compose with PostgreSQL: version: '3.8' services: postgres: image: postgres:16-alpine environment: POSTGRES_USER: directus POSTGRES_DB: directus POSTGRES_PASSWORD: directus volumes: - postgres_data:/var/lib/postgresql/data
directus: image: directus/directus ports: - 8050:8050 environment: KEY: change-me-to-random-secret SECRET: change-me-to-random-secret DB_CLIENT: postgres DB_HOST: postgres DB_PORT: 5432 DB_USER: directus DB_PASSWORD: directus DB_DATABASE: directus depends_on: - postgres volumes: postgres_data:Quickstart with SQLite (Development): docker run -p 8050:8050 directus/directus
Production with persistent data: docker run -d
--name directus
-p 8050:8050
-v directus_data:/directus/data
-e KEY=your-secret-key
directus/directus# docker-compose.yml for Directus with PostgreSQL and Redis version: '3.8' services: postgres: image: postgres:16-alpine ports: - '5432:5432' environment: POSTGRES_USER: directus POSTGRES_DB: directus POSTGRES_PASSWORD: directus-password volumes: - postgres_data:/var/lib/postgresql/data redis: image: redis:alpine ports: - '6379:6379' volumes: - redis_data:/data directus: image: directus/directus:latest ports: - 8050:8050 environment: KEY: your-secret-key-here SECRET: your-secret-key-here DB_CLIENT: postgres DB_HOST: postgres DB_PORT: 5432 DB_USER: directus DB_PASSWORD: directus-password DB_DATABASE: directus CACHE_STORE: redis REDIS_HOST: redis REDIS_PORT: 6379 depends_on: - postgres - redis volumes: postgres_data: redis_data: - Step 4
Configuration and environment variables
Directus is configured through environment variables. The .env file should be in your project root directory.
Core Environment Variables: KEY=your-secret-key-for-session-cryptography SECRET=your-secret-key-for-access-token-signing KEY_LENGTH=32 HOST=0.0.0.0 PORT=8050 PUBLIC_URL=http://localhost:8050
Database Configuration: DB_CLIENT=postgres DB_HOST=localhost DB_PORT=5432 DB_USER=directus DB_PASSWORD=directus DB_DATABASE=directus DB_SSL=.false
Redis for caching: CACHE_STORE=redis REDIS_HOST=localhost REDIS_PORT=6379
File Storage (S3): STORAGE_DRIVERS=s3 STORAGE_S3_ACCESS_KEY_ID=your-aws-access-key STORAGE_S3_SECRET_ACCESS_KEY=your-aws-secret-key STORAGE_S3_BUCKET=your-bucket STORAGE_S3_REGION=us-east-1
Email (SMTP): EMAIL_FROM=noreply@example.com EMAIL_TRANSPORT=smtp EMAIL_TRANSPORT_HOST=smtp.gmail.com EMAIL_TRANSPORT_PORT=587 EMAIL_TRANSPORT_USER=your-email@gmail.com EMAIL_TRANSPORT_PASSWORD=your-app-password
Security: CORS_ENABLED=false RATE_LIMITER_ENABLED=true RATE_LIMITER_POINTS=50 RATE_LIMITER_DURATION=60
# Generate .env file for Directus echo "# Core Configuration KEY=\$(openssl rand -hex 32) SECRET=\$(openssl rand -hex 32) KEY_LENGTH=32 # Server HOST=0.0.0.0 PORT=8050 PUBLIC_URL=http://localhost:8050 # PostgreSQL Database DB_CLIENT=postgres DB_HOST=localhost DB_PORT=5432 DB_USER=directus DB_PASSWORD=your-database-password DB_DATABASE=directus DB_SSL=0 # Cache CACHE_STORE=redis REDIS_HOST=localhost REDIS_PORT=6379 # Upload UPLOAD_MAX_SIZE=100MB # Email EMAIL_FROM=noreply@yourdomain.com EMAIL_TRANSPORT=smtp EMAIL_TRANSPORT_HOST=smtp.gmail.com EMAIL_TRANSPORT_PORT=587 # Security CORS_ENABLED=false RATE_LIMITER_ENABLED=true" > .env - Step 5
Using the Admin App
Once Directus is running, access the admin panel at http://localhost:8050. On first access, you'll be prompted to create an admin account.
Setting up your first project:
-
Create Admin Account:
- Navigate to http://localhost:8050
- Enter admin email and password
- Complete setup wizard
-
Database Schema Discovery:
- Directus automatically scans your database
- Tables appear as "Collections"
- Columns appear as "Fields"
- Relationships are detected automatically
-
Customizing Collections:
- Go to Settings > Data Model
- Click on any collection to edit
- Add, remove, or modify fields
- Configure field types and interfaces
- Set up relationships between collections
-
Managing Content:
- Navigate to the Content menu
- Browse collections
- Create, edit, and delete items
- Use inline editing or modal views
- Search and filter content
-
User Management:
- Go to Settings > Users & Roles
- Create users and assign roles
- Configure role permissions
- Set up OAuth providers
-
File Management:
- Access the Files interface
- Upload files via drag-and-drop
- Organize with folders and metadata
- Generate image transformations
Admin App Interface Areas: • Data: Manage collections and items (database tables) • Files: Upload and organize files/media • Settings: Configure system, roles, permissions • Flows: Create automation workflows • Insights: View analytics and reports • Profile: User settings and preferences First-time Setup Steps: 1. Create admin account 2. Explore auto-discovered collections 3. Customize field interfaces 4. Set up file storage 5. Configure users and roles 6. Create automated flows -
- Step 6
API Usage and Authentication
Directus provides both REST and GraphQL APIs accessible immediately after setup.
REST API Examples:
Getting an access token: POST /auth/login {"email": "admin@example.com", "password": "password"} Response: {"data": {"access_token": "jwt-token"}}
Fetching items: GET /items/articles GET /items/articles?filter[status][_eq]=published&limit=10 GET /items/articles?sort=-created_at&fields=title,excerpt,author
Creating an item: POST /items/articles {"title": "Hello World", "status": "published"}
Updating an item: PATCH /items/articles/123 {"title": "Updated Title"}
Deleting an item: DELETE /items/articles/123
GraphQL API: GraphiQL available at /graphql query { articles(limit: 10) { title author { first_name } } }
Headers for authentication: Authorization: Bearer <your_access_token>
# Python example using requests import requests BASE_URL = "http://localhost:8050" # Login login_response = requests.post( f"{BASE_URL}/auth/login", json={"email": "admin@example.com", "password": "password"} ) token = login_response.json()["data"]["access_token"] headers = {"Authorization": f"Bearer {token}"} # Fetch articles articles_response = requests.get( f"{BASE_URL}/items/articles", headers=headers, params={"limit": 10, "filter[status][_eq]": "published"} ) articles = articles_response.json()["data"] for article in articles: print(f"{article['id']}: {article['title']}") # Create article new_article = {"title": "New Article", "status": "draft"} create_response = requests.post( f"{BASE_URL}/items/articles", headers=headers, json=new_article ) print(f"Created: {create_response.json()['data']['id']}") - Step 7
Creating Collections and Fields
Define your content model through the admin UI or programmatically using the GraphQL/REST APIs.
Creating a Collection via Admin UI:
- Go to Settings > Data Model
- Click "Create Collection"
- Enter collection name (creates a table)
- Configure display templates
- Add fields
Field Types:
- Single Line Input: Text fields like titles
- Text Input: Multi-line content
- Rich Text Editor: WYSIWYG content
- Number: Integer/decimal values
- Boolean: Yes/No flags
- Date & Time: Timestamps and dates
- User/Relation: Relationships between collections
- Files: Media attachments
- JSON: Raw JSON data
- Translation: Multi-language content
Relationship Types:
- Many-to-One: Article→Author
- One-to-Many: Author→Articles
- Many-to-Many: Articles↔Tags
- One-to-One: User→Profile
Creating via API: POST /system/collections {"collection": "products", "meta": {"icon": "shopping_cart", "note": "Product catalog"}}
{ "collection": "articles", "meta": { "icon": "article", "note": "Blog articles collection" }, "fields": [ {"field": "title", "type": "string", "meta": {"name": "Title", "interface": "input"}}, {"field": "slug", "type": "string", "meta": {"name": "Slug", "interface": "input"}}, {"field": "content", "type": "text", "meta": {"name": "Content", "interface": "input-rich-text-md"}}, {"field": "excerpt", "type": "text", "meta": {"name": "Excerpt", "interface": "input"}}, {"field": "author", "type": "alias", "meta": {"name": "Author", "interface": "m2o"}}, {"field": "status", "type": "string", "meta": { "name": "Status", "interface": "select-dropdown", "options": { "choices": [ {"text": "Draft", "value": "draft"}, {"text": "Published", "value": "published"} ] } }}, {"field": "published_at", "type": "timestamp", "meta": {"name": "Published At", "interface": "datetime"}}, {"field": "featured_image", "type": "alias", "meta": {"name": "Featured Image", "interface": "m2o"}} ] } - Step 8
Roles, Permissions, and Security
Directus provides granular role-based access control (RBAC) for collections, fields, and actions.
Permission Types:
- Create: Add new items
- Read: View items
- Update: Edit existing items
- Delete: Remove items
- List: See in list view
- Export: Export data
Advanced Permissions:
- Filter-based permissions (AVO filtering)
- Field-level access restrictions
- IP whitelisting for specific roles
- Rate limiting per role
Creating Roles:
- Navigate to Settings > Users & Roles
- Click "Create Role"
- Define name and description
- Assign permissions per collection
- Set read/update/delete/create permissions
- Configure field-level access
Example Filters:
- Users can only edit their own posts: author = {{CURRENT_USER.ID}}
- Admins access all content: no filter
- Editors see published only: status = published
Authentication Providers:
- Email/Password (default)
- OAuth2: Google, GitHub, Azure AD
- SAML 2.0 for enterprise SSO
- LDAP/Active Directory
- 2FA/TOTP authentication
- API keys for service-to-service
Security Best Practices: 1. Environment: Use strong KEY and SECRET values, never commit .env 2. Access: Follow least privilege principle, enable 2FA for admins 3. API: Use rate limiting, IP whitelisting, rotate API keys 4. Database: Enable encryption, use backups, sanitize inputs 5. Updates: Keep Directus updated, test in staging first 6. CORS: Restrict origins to known domains - Step 9
Flows and Automation
Directus Flows provide a visual automation builder for creating custom workflows without code.
Flow Triggers:
- Scheduled: Cron-like intervals
- Webhook: External events
- Operation: Internal events (item.create, item.update)
- Custom: API-triggered
Operations:
- Notification: Send emails, Slack messages
- Request: HTTP GET/POST/PUT/DELETE
- Action: JavaScript code execution
- Transform: Data manipulation
- Filter: Conditional branching
- Sleep: Delay execution
Creating a Flow:
- Navigate to Settings > Flows
- Click "Create Flow"
- Name your flow
- Add trigger (e.g., "On Item Create")
- Add operations (Send Email, Make Request)
- Configure each operation
- Save and activate
Common Use Cases:
- Auto-generate preview images on publish
- Send notifications on important events
- Sync data to external systems
- Validate incoming data
- Auto-assign tasks based on conditions
{ "flow-example": { "name": "New Article Notification", "trigger": { "type": "event", "name": "onCreate", "scope": ["items.create.articles"] }, "operations": [ { "key": "check-status", "name": "Check Status", "type": "filter", "operator": "equals", "left": "{{ $trigger.payload.data.status }}", "right": "published" }, { "key": "send-email", "name": "Send Email to Team", "type": "action", "operation": { "name": "email", "options": { "to": "team@company.com", "subject": "New Article: {{ $trigger.payload.data.title }}" } }, "run_if": "{{ $check-status }}" }, { "key": "slack-notification", "name": "Slack Notification", "type": "action", "operation": { "name": "request", "options": { "destination": "https://hooks.slack.com/services/YOUR/WEBHOOK", "method": "POST", "data": {"text": "New article: {{ $trigger.payload.data.title }}"} } }, "run_if": "{{ $check-status }}" } ], "active": true } } - Step 10
Extensions and Customization
Directus is highly extensible with its plugin/extension system.
Extension Types:
- Modules: Top-level navigation items
- Dashboards: Custom analytics and reporting
- Layouts: Custom data grid layouts
- Interfaces: Custom field editing components
- Displays: Custom field display components
- Operations: Custom flow operations
- Permissions: Custom permission checks
- Endpoints: Custom API routes
Creating a Custom Extension:
- Install CLI: npm install -g @directus/create-extension
- Create: directus create
- Select extension type
- Develop with: npm run dev
- Build with: npm run build
- Package and distribute
Sharing Extensions:
- Self-hosted extension registry
- GitHub packages
- npm publish
- Community directory
Extension package.json example: { "name": "@mycompany/directus-extension", "version": "1.0.0", "type": "module", "scripts": { "dev": "extension dev", "build": "extension build" }, "devDependencies": { "@directus/extensions-sdk": "^14.0.0" } } Common extensions: - Custom auth providers - External API integrations - Custom UI themes - Workflow interfaces - Custom exports - Step 11
Storage and File Management
Directus provides robust file management with multiple storage drivers and on-the-fly image transformations.
Storage Drivers:
- Local: Server filesystem
- S3: AWS S3 and compatible (Minio, DigitalOcean)
- Azure: Azure Blob Storage
- GCS: Google Cloud Storage
- Cloudinary: CDN platform
- Supabase: Supabase Storage
Configuring S3: STORAGE_DRIVERS=s3 STORAGE_S3_ACCESS_KEY_ID=AWS_KEY STORAGE_S3_SECRET_ACCESS_KEY=AWS_SECRET STORAGE_S3_BUCKET=bucket-name STORAGE_S3_REGION=us-east-1
Upload Features:
- Drag and drop interface
- Bulk uploads with progress
- Resumable uploads (TUS)
- Auto image optimization
- Metadata extraction (EXIF)
- File validation
Image Transformations:
- Resize: /assets/file-id?width=800
- Crop: /assets/file-id?width=400&height=400&crop=fit
- Format: /assets/file-id?format=webp
- Effects: /assets/file-id&blur=10&grayscale=1
- Combined: Multiple params together
# Storage Configuration Examples # Local storage echo "STORAGE_UPLOADS_DRIVER=local STORAGE_UPLOADS_ROOT=./uploads" >> .env # S3 storage echo "STORAGE_UPLOADS_DRIVER=s3 STORAGE_UPLOADS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE STORAGE_UPLOADS_SECRET_ACCESS_KEY=secret STORAGE_UPLOADS_BUCKET=my-bucket STORAGE_UPLOADS_REGION=us-east-1" >> .env # Azure storage echo "STORAGE_UPLOADS_DRIVER=azure STORAGE_ACCOUNT=mystorage STORAGE_CONTAINER=uploads STORAGE_SHARED_KEY=your-key" >> .env # Multiple locations echo "STORAGE_LOCATIONS=uploads,archive STORAGE_UPLOADS_DRIVER=s3 STORAGE_ARCHIVE_DRIVER=local" >> .env # Image settings echo "IMAGINE_AUTOOrient=true IMAGINE_JPGQuality=80 IMAGINE_WEBPQuality=80 UPLOAD_MAX_SIZE=100MB" >> .env # CDN IMAGE_CDN=https://cdn.example.com - Step 12
Hooks and Events
Directus hooks allow you to inject code at various lifecycle points.
Hook Types:
Application Level:
- init: When Directus starts
- action: Before certain actions
- filter: Modify results
- request: Incoming HTTP requests
- response: Outgoing responses
Item Operations:
- item.create.before/after
- item.read.before/after
- item.update.before/after
- item.delete.before/after
Authentication:
- login.before/after
- login.failed
Webhook Events:
- Item create/update/delete
- User events
- Role/permission changes
- Flow triggers
# Webhook server example # webhook-server.js - Node.js const express = require('express'); const app = express(); app.use(express.json()); app.post('/directus-webhook', (req, res) => { const { action, collection, payload } = req.body; console.log(`[${action}] ${collection}:`, payload); if (action === 'create' && collection === 'articles') { console.log('New article:', payload.data); // Trigger custom logic } res.status(200).json({ received: true }); }); app.listen(3001, () => { console.log('Webhook server on 3001'); }); # Register in Directus: # Settings > Webhooks > Create # - Type: Event # - Condition: collection = articles # - Address: http://your-server/directus-webhook - Step 13
Production Deployment and Best Practices
Deploying Directus to production requires careful configuration.
Production Checklist:
-
Environment:
- Strong KEY and SECRET values
- Production database credentials
- SSL/TLS enabled
- Proper PUBLIC_URL
-
Database:
- PostgreSQL recommended
- Connection pooling
- Automated backups
- Proper indexing
-
Caching:
- Redis for production
- Appropriate TTL values
- Cache invalidation strategy
-
File Storage:
- Cloud storage (S3/Azure/GCS)
- CDN for assets
- CORS headers
-
Security:
- HTTPS enabled
- CORS configured
- Rate limiting
- Regular updates
-
Monitoring:
- Error logging
- Performance metrics
- Health checks
- Alerting
-
Scaling:
- PM2 or orchestration
- Load balancers
- Stateless design
- Horizontal scaling
# Production Docker Compose version: '3.8' services: postgres: image: postgres:16-alpine restart: unless-stopped environment: POSTGRES_USER: directus_prod POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password POSTGRES_DB: directus_prod volumes: - postgres_data:/var/lib/postgresql/data redis: image: redis:7-alpine restart: unless-stopped command: redis-server --requirepass \${REDIS_PASSWORD} volumes: - redis_data:/data directus: image: directus/directus:latest restart: unless-stopped ports: - "3333:3333" environment: KEY: \${DIRECTUS_KEY} SECRET: \${DIRECTUS_SECRET} DB_CLIENT: postgres DB_HOST: postgres DB_PORT: 5432 DB_USER: directus_prod DB_PASSWORD_FILE: /run/secrets/db_password DB_DATABASE: directus_prod CACHE_STORE: redis REDIS_HOST: redis REDIS_PORT: 6379 NODE_ENV: production PUBLIC_URL: https://your-domain.com STORAGE_UPLOADS_DRIVER: s3 STORAGE_UPLOADS_ACCESS_KEY_ID: \${AWS_ACCESS_KEY_ID} STORAGE_UPLOADS_SECRET_ACCESS_KEY: \${AWS_SECRET_ACCESS_KEY} STORAGE_UPLOADS_BUCKET: \${AWS_S3_BUCKET} depends_on: - postgres - redis healthcheck: test: ["CMD", "wget", "--spider", "http://localhost:3333/server/ping"] interval: 30s timeout: 10s retries: 3 volumes: postgres_data: redis_data: -
- Step 14
Resources and Documentation
Official Resources:
- Website: https://directus.io
- Documentation: https://docs.directus.io
- GitHub: https://github.com/directus/directus
- Demo: https://demo.directus.io
- Blog: https://directus.io/blog
Developer Resources:
- API Documentation: https://docs.directus.io/reference/
- GraphQL: https://docs.directus.io/reference/graphql/
- Extensions: https://docs.directus.io/extensions/
- SDK: https://github.com/directus/sdk
Community:
- Discord: https://directus.org/discord
- Forum: https://forum.directus.io
- Stack Overflow: directus tag
- GitHub Discussions
Learning:
- Video Tutorials: YouTube channel
- Extension Marketplace
- Template Projects
Database Compatibility:
- PostgreSQL 13+
- MySQL 8+ / MariaDB 10.6+
- SQLite 3.36+
- SQL Server 2019+
- CockroachDB
- Oracle 21+
Version: 36.x Node.js: >= 22 License: MIT (Core)
Quick Links: Website: https://directus.io Docs: https://docs.directus.io GitHub: https://github.com/directus/directus Demo: https://demo.directus.io Discord: https://directus.org/discord Forum: https://forum.directus.io Blog: https://directus.io/blog SDK: https://github.com/directus/sdk NPM: https://www.npmjs.com/package/directus Docker: https://hub.docker.com/r/directus/directus Enterprise: https://directus.io/contact/enterprise Stats: - Stars: 36,000+ on GitHub - Forks: 4,700+ - Contributors: 500+ - License: MIT
Feature requests
Sign in to suggest features or vote on existing ones.
No feature requests yet.
Discussion
Sign in to join the discussion.
No comments yet.