Why self-host Directus on a VPS
Directus plugs into your existing database (PostgreSQL, MySQL, SQLite, MariaDB) without imposing its own schema: it introspects your tables and generates an API and an admin interface on top. By self-hosting it on a VPS, you avoid the per-seat pricing of the official Cloud offering and you keep your content, your media, and your SQL database on your own infrastructure. It's a structuring choice for agencies that deliver several sites: a single Directus can serve as the back office for multiple front-ends (Nuxt, Next, mobile). Control of the database, extensions, and webhooks lets you integrate Directus into any data pipeline without depending on a third-party provider, all while benefiting from low latency for your local users.
The concrete benefits of self-hosting Directus
- REST and GraphQL APIs generated automatically from your SQL schema, without writing code.
- A no-code admin interface to model content, manage roles, and set granular permissions.
- Compatible with an existing SQL database: you can expose it without migrating or altering it.
- Media management, on-the-fly image transformations, and local or S3 storage of your choice.
- Built-in Flows and webhooks to automate processing (notifications, synchronizations).
- Pooling: a single Directus VPS can power several sites and front-end applications.
Hardware and software prerequisites
Directus is a Node.js application that relies on an external database. Count on a minimum of 2 GB of RAM to comfortably run Directus and PostgreSQL on the same VPS; 4 GB are recommended in production with image transformations and several simultaneous administrators. One to two vCPU are enough to start. Plan for a Redis cache (optional but recommended for sessions and rate limiting) and a 30 to 60 GB SSD volume depending on your media library. You'll need Docker and Docker Compose, a domain pointing to the VPS, and a robust secret key for SECRET and token hashing. Postgres remains the recommended database for advanced features.
Deploying Directus step by step
Provision the VPS and Docker
After connecting via SSH, install Docker and Docker Compose. Create an A DNS record for cms.mydomain.com pointing to the VPS IP. Open only ports 80 and 443 in the firewall (ufw allow 80,443/tcp).
Define the stack in docker-compose.yml
Declare three services: database (image postgres:16 with a persistent volume), cache (redis), and directus (directus/directus). Connect Directus to Postgres via the variables DB_CLIENT=pg, DB_HOST=database, DB_DATABASE, DB_USER, DB_PASSWORD.
Configure the environment variables
Fill in KEY and SECRET with random strings (openssl rand -hex 32), set ADMIN_EMAIL and ADMIN_PASSWORD for the first account, and PUBLIC_URL=https://cms.mydomain.com so that links and uploads are correct.
Start and initialize the database
Run docker compose up -d. On the first startup, Directus automatically creates the system tables and the admin account. Follow the initialization with docker compose logs -f directus.
Connect the reverse proxy and TLS
Put Caddy or Traefik in front of Directus to handle Let's Encrypt. With Traefik, add the routing labels on the directus service and the ACME certresolver. Make sure PUBLIC_URL exactly matches the HTTPS domain to avoid CORS and upload issues.
Secure and back up
Restrict the CORS origins to your front-ends' domains, disable public registration if unnecessary, then schedule a daily pg_dump plus a copy of the uploads folder to remote storage.
To serve images performantly, enable media storage on an S3-compatible bucket rather than the local disk, and cache the image transformations (ASSETS_CACHE_TTL). Coupled with a CDN in front of Directus, this offloads media requests from the VPS and speeds up front-end rendering. Also consider limiting the size of allowed transformations via ASSETS_TRANSFORM_MAX_OPERATIONS to prevent abuse of on-the-fly image generation.