Directus is a powerful headless CMS that provides a flexible data platform with a beautiful admin app for managing content. In this guide, we’ll walk through the process of setting up Directus on your own server using Docker and Docker Compose.
Introduction
Directus wraps any SQL database with a real-time GraphQL+REST API and an intuitive app for non-technical users. Using Docker with Directus provides several advantages:
- Consistent development environments
- Easy deployment and scaling
- Simplified dependency management
- Isolation from the host system
This guide covers a complete setup with PostgreSQL as the database, though Directus supports other database systems as well.
Prerequisites
Before you begin, ensure you have:
- A server with Docker installed (v20.10.0+)
- Docker Compose installed (v2.0.0+)
- Basic understanding of terminal/command line
- Minimum system requirements: 1x 0.25 vCPU / 512 MB (recommended: 2x 1 vCPU / 2GB)
- Port 8055 available for Directus (or configure a different port)
Basic Docker Compose Setup
Let’s start with a basic docker-compose.yml
file that sets up Directus with PostgreSQL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
version: '3' services: directus: image: directus/directus:latest ports: - "8055:8055" volumes: - ./directus/uploads:/directus/uploads - ./directus/extensions:/directus/extensions environment: # Database configuration DB_CLIENT: 'pg' DB_HOST: 'database' DB_PORT: '5432' DB_DATABASE: 'directus' DB_USER: 'directus' DB_PASSWORD: 'directus' # Security keys (generate with 'openssl rand -base64 32') KEY: '255d861b-5ea1-5996-9c3b-133a3f4d8035' SECRET: '6116487b-cda1-52c2-b5b5-c8022c45e383' # Admin user creation (only needed on first run) ADMIN_EMAIL: 'admin@example.com' ADMIN_PASSWORD: 'changeme123' # Other options PUBLIC_URL: 'http://localhost:8055' depends_on: - database restart: unless-stopped database: image: postgis/postgis:14-master volumes: - ./data/database:/var/lib/postgresql/data environment: POSTGRES_USER: 'directus' POSTGRES_PASSWORD: 'directus' POSTGRES_DB: 'directus' restart: unless-stopped |
Save this file as docker-compose.yml
in a new directory on your server.
Essential Environment Variables
Database Configuration
Variable | Example | Description |
---|---|---|
DB_CLIENT | pg | Database client (pg, mysql, sqlite3, cockroachdb, mssql, oracledb) |
DB_HOST | database | Database host (service name from docker-compose) |
DB_PORT | 5432 | Database port (5432 for PostgreSQL) |
DB_DATABASE | directus | Database name |
DB_USER | directus | Database username |
DB_PASSWORD | directus | Database password |
Security Keys
These keys are used for encryption and JWT token generation. Generate strong random strings:
Variable | Example | Description |
---|---|---|
KEY | 255d861b-5ea1-5996-9c3b-133a3f4d8035 | Random string for encryption |
SECRET | 6116487b-cda1-52c2-b5b5-c8022c45e383 | Random string for JWT tokens |
You can generate these using the command: openssl rand -base64 32
Admin User Configuration
These variables are only needed on the first run to create the admin user:
Variable | Example | Description |
---|---|---|
ADMIN_EMAIL | admin@example.com | Email for the admin user |
ADMIN_PASSWORD | changeme123 | Password for the admin user |
After the first successful run, you can remove these from your environment variables for security.
Public URL Configuration
Variable | Example | Description |
---|---|---|
PUBLIC_URL | http://localhost:8055 | Public URL where Directus will be accessible |
Advanced Configuration Options
Cache Configuration
Redis is recommended for production environments:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
services: directus: # ... other configuration environment: # ... other variables CACHE_ENABLED: 'true' CACHE_STORE: 'redis' CACHE_REDIS: 'redis://cache:6379' depends_on: - database - cache cache: image: redis:alpine restart: unless-stopped |
Variable | Example | Description |
---|---|---|
CACHE_ENABLED | true | Enable or disable caching |
CACHE_STORE | redis | Cache storage type (memory, redis) |
CACHE_REDIS | redis://cache:6379 | Redis connection string |
CACHE_TTL | 300 | Time to live in seconds |
CACHE_NAMESPACE | directus | Cache namespace to avoid conflicts |
CORS Settings
If you need to configure CORS for your API:
Variable | Example | Description |
---|---|---|
CORS_ENABLED | true | Enable or disable CORS |
CORS_ORIGIN | * | Allowed origins (use specific in production) |
CORS_METHODS | GET,POST,PATCH,DELETE | Allowed HTTP methods |
CORS_ALLOWED_HEADERS | Content-Type,Authorization | Allowed HTTP headers |
CORS_EXPOSED_HEADERS | Content-Range | Headers exposed to the browser |
CORS_CREDENTIALS | true | Allow credentials (cookies, authorization headers) |
CORS_MAX_AGE | 18000 | Preflight cache time in seconds |
File Storage
For production, you might want to use S3 compatible storage instead of local storage:
Variable | Example | Description |
---|---|---|
STORAGE_LOCATIONS | amazon | Storage location name |
STORAGE_AMAZON_DRIVER | s3 | Storage driver to use (s3, local, etc.) |
STORAGE_AMAZON_ROOT | /uploads | Root path for uploads |
STORAGE_AMAZON_KEY | your-access-key | S3 access key |
STORAGE_AMAZON_SECRET | your-secret-key | S3 secret key |
STORAGE_AMAZON_BUCKET | your-bucket-name | S3 bucket name |
STORAGE_AMAZON_REGION | us-east-1 | S3 region |
STORAGE_AMAZON_ENDPOINT | https://s3.amazonaws.com | S3 endpoint URL |
Email Configuration
To enable email functionality:
Variable | Example | Description |
---|---|---|
EMAIL_TRANSPORT | smtp | Email transport to use |
EMAIL_SMTP_HOST | smtp.example.com | SMTP server host |
EMAIL_SMTP_PORT | 587 | SMTP server port |
EMAIL_SMTP_USER | user | SMTP username |
EMAIL_SMTP_PASSWORD | password | SMTP password |
EMAIL_FROM | no-reply@example.com | Default from email address |
EMAIL_SECURE | false | Use TLS (true for port 465, false for other ports) |
Additional Configuration Options
Authentication Options
Variable | Example | Description |
---|---|---|
AUTH_PROVIDERS | github,google | Enabled auth providers (comma separated) |
AUTH_GITHUB_DRIVER | oauth2 | Auth driver for GitHub |
AUTH_GITHUB_CLIENT_ID | your-client-id | GitHub OAuth client ID |
AUTH_GITHUB_CLIENT_SECRET | your-client-secret | GitHub OAuth client secret |
AUTH_GITHUB_AUTHORIZE_URL | https://github.com/login/oauth/authorize | GitHub authorization URL |
AUTH_GITHUB_ACCESS_URL | https://github.com/login/oauth/access_token | GitHub access token URL |
AUTH_GITHUB_PROFILE_URL | https://api.github.com/user | GitHub user profile URL |
AUTH_GITHUB_ALLOW_PUBLIC_REGISTRATION | true | Allow new users to register through GitHub |
Rate Limiting
Variable | Example | Description |
---|---|---|
RATE_LIMITER_ENABLED | true | Enable rate limiting |
RATE_LIMITER_STORE | redis | Where to store rate limiter data |
RATE_LIMITER_POINTS | 50 | Number of allowed requests per duration |
RATE_LIMITER_DURATION | 1 | Duration in seconds |
RATE_LIMITER_GLOBAL | false | Use a single global rate limiter |
Logging Options
Variable | Example | Description |
---|---|---|
LOG_LEVEL | info | Log level (trace, debug, info, warn, error, fatal) |
LOG_STYLE | pretty | Log style (pretty, raw, json) |
LOG_HTTP_ENABLED | true | Enable HTTP request logging |
LOG_HTTP_LOG_STATUS | true | Include HTTP status in logs |
Telemetry
Variable | Example | Description |
---|---|---|
TELEMETRY | false | Enable or disable telemetry |
Extensions
Variable | Example | Description |
---|---|---|
EXTENSIONS_PATH | /directus/extensions | Path to extensions directory |
EXTENSIONS_AUTO_RELOAD | true | Auto reload extensions when changed |
Persistent Storage and Volumes
In the Docker Compose setup, we’ve defined several volumes to ensure data persistence:
1 2 3 4 5 6 |
volumes: - ./directus/uploads:/directus/uploads # For file uploads - ./directus/extensions:/directus/extensions # For custom extensions - ./data/database:/var/lib/postgresql/data # For database files |
Make sure the host directories exist and have proper permissions:
1 2 3 4 |
mkdir -p ./directus/uploads ./directus/extensions ./data/database chmod -R 755 ./directus ./data |
Security Considerations
- Use specific version tags: Instead of
latest
, use specific version tags likedirectus/directus:10.8.2
to avoid unexpected changes. - Never expose database ports: Unless necessary, don’t expose database ports to the public.
- Use strong passwords and keys: Generate secure random strings for all passwords and keys.
- Remove admin credentials after first run: Delete the
ADMIN_EMAIL
andADMIN_PASSWORD
environment variables after the first successful setup. - Set up HTTPS: For production, always use HTTPS. You can use a reverse proxy like Nginx with Let’s Encrypt.
Production-Ready Setup with Nginx
For production environments, it’s recommended to use Nginx as a reverse proxy with SSL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
version: '3' services: directus: image: directus/directus:10.8.2 volumes: - ./directus/uploads:/directus/uploads - ./directus/extensions:/directus/extensions environment: DB_CLIENT: 'pg' DB_HOST: 'database' DB_PORT: '5432' DB_DATABASE: 'directus' DB_USER: 'directus' DB_PASSWORD: 'strong_password_here' KEY: 'generated_key_here' SECRET: 'generated_secret_here' PUBLIC_URL: 'https://directus.yourdomain.com' CACHE_ENABLED: 'true' CACHE_STORE: 'redis' CACHE_REDIS: 'redis://cache:6379' # First run only # ADMIN_EMAIL: 'admin@example.com' # ADMIN_PASSWORD: 'strong_admin_password' depends_on: - database - cache restart: unless-stopped networks: - directus-network database: image: postgis/postgis:14-master volumes: - ./data/database:/var/lib/postgresql/data environment: POSTGRES_USER: 'directus' POSTGRES_PASSWORD: 'strong_password_here' POSTGRES_DB: 'directus' restart: unless-stopped networks: - directus-network cache: image: redis:alpine restart: unless-stopped networks: - directus-network nginx: image: nginx:alpine ports: - "80:80" - "443:443" volumes: - ./nginx/conf:/etc/nginx/conf.d - ./nginx/ssl:/etc/nginx/ssl - ./nginx/html:/usr/share/nginx/html depends_on: - directus restart: unless-stopped networks: - directus-network networks: directus-network: |
Example Nginx configuration for /nginx/conf/default.conf
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
server { listen 80; server_name directus.yourdomain.com; location / { return 301 https://$host$request_uri; } } server { listen 443 ssl; server_name directus.yourdomain.com; ssl_certificate /etc/nginx/ssl/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/privkey.pem; location / { proxy_pass http://directus:8055; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_cache_bypass $http_upgrade; } } |
Starting Directus
To start your Directus instance:
1 2 3 |
docker-compose up -d |
This will download the necessary images and start the containers. Directus will be available at http://localhost:8055
(or your configured URL).
To check the logs:
1 2 3 |
docker-compose logs -f directus |
Conclusion
With this guide, you should now have a fully functioning Directus instance running on your own server using Docker Compose. You can customize the configuration further based on your specific requirements and take advantage of Directus’s powerful features for content management.
Remember to:
- Regularly backup your database
- Keep your Docker images updated
- Monitor server resources
- Follow security best practices
For more information, refer to the official Directus documentation at docs.directus.io.
FAQ
What is Directus?
Directus is a flexible backend solution and headless CMS that sits on top of your SQL database. It provides instant REST and GraphQL APIs, a customizable admin app, and advanced features like user management, asset handling, and workflow automation without requiring any coding. Directus can be used for websites, apps, internal tools, or data management systems.
Is Directus open source?
Directus is licensed under the Business Source License (BSL) 1.1 with additional usage grants. This means it functions like open source for most users. If your organization has less than $5M in annual revenue/funding, you can use Directus freely. For larger organizations using Directus in production, a commercial license is required. The complete codebase is available on GitHub to download, contribute to, or fork.
What databases does Directus support?
Directus supports multiple SQL databases, including: PostgreSQL, MySQL, SQLite, OracleDB, CockroachDB, MariaDB, Microsoft SQL Server, and AWS Aurora. It works with both new and existing databases with no migration required.
How does Directus handle installation and deployment?
Directus can be deployed in multiple ways: via Docker (recommended for most users), self-hosted on your own infrastructure, or using Directus Cloud (a managed service starting at $15/month). For Docker installation, you’ll need to create a docker-compose.yml file with your configuration settings. Self-hosting requires Node.js and your chosen database to be set up beforehand.
What are the key features of Directus?
Key features include: instant REST and GraphQL APIs, a customizable admin interface, digital asset management with on-the-fly image transformations, user management with granular permissions, multilingual content support (64+ languages), data versioning, automations and workflows, dashboards and insights, and direct database access for advanced queries.
What is a headless CMS and how does Directus function as one?
A headless CMS separates content management (backend) from presentation (frontend). Directus functions as a headless CMS by providing an intuitive interface for content management while delivering content via APIs that can be consumed by any frontend technology. This enables omnichannel content delivery across websites, apps, IoT devices, and more, while giving developers freedom to build with their preferred tools and frameworks.
How does Directus handle security?
Directus provides comprehensive security features including: granular role-based access controls, field-level permissions, IP allowlists, configurable public access, authentication via email/password, SSO (OAuth 2.0, OpenID, LDAP, SAML), data encryption, and secure API access. By default, all permissions are turned off for the Public role, ensuring your content is only accessible to authenticated users unless explicitly configured otherwise.
How is Directus different from traditional CMS platforms?
Unlike traditional CMS platforms that combine content management with frontend presentation in proprietary formats, Directus is “pure” and database-first. It sits on top of your SQL database without altering it, provides API access to your data, and separates content from presentation. This gives developers complete freedom in choosing frontend technologies, enables omnichannel content delivery, improves performance, and eliminates vendor lock-in.
What programming languages and technologies does Directus use?
Directus is built entirely in TypeScript. The backend API is built on Node.js, while the admin app (Data Studio) is built with Vue.js. Earlier versions of Directus (pre-version 9) were written in PHP, but the current version is built on Node.js for better performance.
How does Directus handle configuration?
Directus uses environment variables for all configuration. These can be defined in a .env file, as system environment variables, or in configuration files (JSON, YAML, JS). Key configuration areas include database connection settings, file storage options, authentication methods, caching, and API settings. For Docker deployments, these are typically defined in the docker-compose.yml file under the environment section.
What are Collections and Items in Directus?
In Directus terminology, a Collection is equivalent to a database table, and an Item is equivalent to a database record or row. Directus uses these more user-friendly terms to make the platform more accessible to non-technical users. Similarly, Fields correspond to database columns, and Types correspond to datatypes.
How does Directus handle file storage and assets?
Directus includes a built-in digital asset manager that supports multiple storage adapters. By default, files are stored in the local filesystem, but Directus also supports cloud storage options like Amazon S3, Google Cloud Storage, and Azure Blob Storage. Features include on-the-fly image transformations, file organization with folders and tags, metadata extraction (IPTC, EXIF, ICC), and relational connections between assets and other content.
Does Directus support multi-language content?
Yes, Directus supports multilingual content in two ways. First, the admin interface itself is available in over 64 languages. Second, Directus allows you to manage translated content for any number of languages in your database, making it easy to deliver localized experiences across all your digital platforms.
What are the system requirements for Directus?
For self-hosting, Directus requires Node.js and a supported SQL database (PostgreSQL, MySQL, SQLite, etc.). The required PHP extensions include: pdo, mysql, curl, gd, fileinfo, mbstring, and xml. For multi-container deployments, Redis is required to ensure features like caching, rate-limiting, and WebSockets work reliably. Docker installation is recommended as it handles all dependencies automatically.
How can I extend or customize Directus?
Directus is fully extensible and customizable. You can create custom interfaces, layouts, displays, modules, and endpoints. The admin app supports custom branding, and you can build custom dashboards for your team. Extensions can be developed using the Directus Extension SDK, and hooks/webhooks can be used to integrate with external services or trigger custom logic.
How much does Directus cost?
Directus is free to use for organizations with less than $5M in annual revenue/funding, regardless of how you use it. For larger organizations using Directus in production, a commercial license is required. Directus Cloud, the managed hosting option, starts at $15/month. Enterprise plans with advanced features and dedicated support are available by contacting the Directus sales team.
How do I update Directus?
For Docker installations, update the image version in your docker-compose.yml file and run docker-compose up -d to apply the update. For npm installations, update the package versions and run npx directus database migrate:latest to update your database structure. Always check the release notes for breaking changes before updating, and make sure to back up your database before major version upgrades.
Can Directus be used with static site generators?
Yes, Directus works excellently with static site generators like Next.js, Nuxt, SvelteKit, Astro, Gatsby, and others. Directus provides the content management interface and APIs, while the static site generator fetches content during build time or via client-side rendering. This combination creates fast, secure websites with a user-friendly admin interface for content editors.
Does Directus provide caching capabilities?
Yes, Directus has built-in data caching options that can dramatically improve API performance. When enabled, Directus caches the output of requests based on the current user and query parameters. It also provides proper cache-control headers for browser and CDN caching. Additionally, internal schema caching (enabled by default) speeds up overall performance by avoiding repeated database introspection.
What are the advantages of using Directus as a headless CMS?
Advantages include: complete technology freedom for developers, omnichannel content delivery, improved performance and scalability, enhanced security, better content reusability, easier content internationalization, direct database access, no vendor lock-in, and a more future-proof architecture that can adapt to changing technology requirements.