Directus is unusual among headless CMSes: instead of defining content models in code, it introspects an existing SQL database and gives you an instant API and admin UI on top. That makes deployment mostly a matter of pointing it at a database and configuring it correctly through environment variables. The official Docker image does the heavy lifting.
How Directus is configured
Directus has effectively zero config files for a basic deploy — everything is environment variables. The essentials:
# Database
DB_CLIENT=pg
DB_CONNECTION_STRING=${DATABASE_URL}
# Security (generate strong random values)
KEY=<random>
SECRET=<random>
# First admin
ADMIN_EMAIL=admin@example.com
ADMIN_PASSWORD=<strong-password>
# Public URL (needed for OAuth redirects, asset URLs)
PUBLIC_URL=https://cms.example.comGenerate KEY and SECRET:
openssl rand -base64 32 # KEY
openssl rand -base64 32 # SECRETSECRET signs access tokens; if it changes, all sessions are invalidated. Treat it as a stable, protected secret.
The official image
You rarely need a custom Dockerfile — pin the official image by version:
# docker-compose.yml (for local parity)
services:
directus:
image: directus/directus:11
ports: ['8055:8055']
environment:
DB_CLIENT: pg
DB_CONNECTION_STRING: ${DATABASE_URL}
KEY: ${KEY}
SECRET: ${SECRET}
ADMIN_EMAIL: ${ADMIN_EMAIL}
ADMIN_PASSWORD: ${ADMIN_PASSWORD}
PUBLIC_URL: ${PUBLIC_URL}Directus runs migrations on its own database tables automatically at boot, so there is no separate migration step for the system schema. Your *content* tables are just regular SQL tables you can manage however you like.
File storage
Like any container app, Directus must not store uploads on local disk. Configure an S3-compatible backend:
STORAGE_LOCATIONS=s3
STORAGE_S3_DRIVER=s3
STORAGE_S3_KEY=<key>
STORAGE_S3_SECRET=<secret>
STORAGE_S3_BUCKET=my-directus-assets
STORAGE_S3_REGION=us-east-1
STORAGE_S3_ENDPOINT=https://s3.amazonaws.comThis works with AWS S3, Cloudflare R2, or MinIO. Without it, every container restart loses uploaded files.
Caching and rate limiting with Redis
For anything beyond a single replica, add Redis so caching and rate limiting are shared:
CACHE_ENABLED=true
CACHE_STORE=redis
REDIS=${REDIS_URL}
RATE_LIMITER_ENABLED=true
RATE_LIMITER_STORE=redisWith Redis, you can run multiple Directus replicas behind a load balancer and they will share cache and rate-limit state. Without it, each replica caches independently and rate limits are per-instance.
Deploying on PandaStack
Directus's environment-driven design fits a managed platform nicely:
- 1Provision a managed PostgreSQL instance. PandaStack injects
DATABASE_URL; map it toDB_CONNECTION_STRING. - 2Optionally provision Redis for caching and rate limiting in multi-replica setups.
- 3Deploy
directus/directus:11as a container app (point the service at the public image, or use a thin Dockerfile thatFROMs it). - 4Set
KEY,SECRET,ADMIN_EMAIL,ADMIN_PASSWORD,PUBLIC_URL, and the S3 variables. - 5Expose port
8055, attach a custom domain, and let SSL provision automatically.
A thin Dockerfile if your platform builds from a repo:
FROM directus/directus:11That's it — all behavior comes from environment variables.
Production checklist
- [ ] Stable
KEYandSECRET(rotating them logs everyone out). - [ ] S3-compatible storage configured.
- [ ]
PUBLIC_URLset to the real external URL. - [ ] Redis for cache + rate limiting if running >1 replica.
- [ ] Pin the image to a major version, not
latest. - [ ] Change or remove
ADMIN_PASSWORDafter first boot and manage admins in-app.
Verifying
# Server info / health
curl -s https://cms.example.com/server/health
# Admin app
curl -s -o /dev/null -w '%{http_code}' https://cms.example.com/adminA healthy /server/health and a 200 on /admin confirm the database connection and asset config are good.
References
- [Directus environment variables reference](https://docs.directus.io/self-hosted/config-options.html)
- [Directus Docker guide](https://docs.directus.io/self-hosted/docker-guide.html)
- [Directus file storage configuration](https://docs.directus.io/self-hosted/config-options.html#file-storage)
- [Directus caching docs](https://docs.directus.io/self-hosted/config-options.html#cache)
---
Directus plus a managed PostgreSQL is a two-step deploy on PandaStack, with DATABASE_URL injected and SSL handled for you. Start free at [dashboard.pandastack.io](https://dashboard.pandastack.io).