Journal indexDockup / field note
Note / self-host-directus

How to Self-Host Directus in 2026: Database, Uploads and Public URL

Self-host Directus with correct ports, persistent storage, HTTPS, secrets, backups and upgrade checks. Learn how to fix when the database client is wrong.

Treat Directus as a small system, not a Docker image. The user-facing goal for Directus is clear: REST and GraphQL API plus an admin interface over your data; the deployment is acceptable only when you can bootstrap the administrator, create a collection and role, write through REST, query through GraphQL and upload a file.

That distinction catches the failure mode operators meet after local testing: the database client is wrong or upload storage is not writable. It also makes the backup and upgrade plan specific enough to test.

Prove Directus survives replacement

A container image can be downloaded again; database, uploads, extensions, flows and schema snapshots cannot. Mount /directus/database before bootstrap, write harmless sample data and replace the container to prove that path is actually persistent. Inspect the effective mount instead of trusting a Compose filename, and check that the runtime user can write where Directus expects.

Choose retention and an off-host destination, then rehearse recovery without touching production. The drill passes only when schema, roles, flows, items, extensions and uploads return and both REST and GraphQL probes succeed. For database-backed state, pair storage snapshots with application-consistent exports as described in point-in-time recovery versus snapshots.

The production shape of Directus

Draw three boundaries around Directus: ingress to port 8055, durable state and supporting requirements. The container is replaceable, but the other two need explicit owners. The network contract for Directus is Postgres plus optional Redis and object storage for scaled deployments. Keep private endpoints on internal DNS, permit only required outbound calls and give Directus a scoped service credential.

The diagram is complete when a clean client can bootstrap the administrator, create a collection and role, write through REST, query through GraphQL and upload a file. Capture timing and resource data for database connection pool, API request concurrency, Flow workers, thumbnail generation and upload storage. If the transaction fails, the first boundary that does not behave as documented identifies whether to investigate routing, local capacity or a supporting service.

Prove the Directus deployment end to end

Create a small, disposable Directus fixture and keep it for every release. The fixture should exercise the real workflow: bootstrap the administrator, create a collection and role, write through REST, query through GraphQL and upload a file. Record the image digest, external hostname, dependency address and the expected result so a later operator can repeat the test without interpreting this guide.

Run the fixture three times. First, use the fresh deployment. Second, replace the container without touching durable state. Third, restore the backup into an empty environment. The third run passes only when schema, roles, flows, items, extensions and uploads return and both REST and GraphQL probes succeed. During each run, capture latency and resource use around database connection pool, API request concurrency, Flow workers, thumbnail generation and upload storage; this becomes the baseline for alerts rather than an arbitrary CPU percentage.

Finally, test the negative path deliberately: temporarily deny the test identity access to Postgres plus optional Redis and object storage for scaled deployments. Confirm that Directus fails visibly without corrupting state, restore the correct condition and repeat the successful transaction. A release record containing those four outcomes is stronger evidence than screenshots of a dashboard or a one-time curl response.

Launch Directus with observable defaults

Start Directus in a way that leaves the route private until bootstrap is complete.

docker run -d \
  --name directus \
  --restart unless-stopped \
  -p 127.0.0.1:8055:8055 \
  -v directus-data:/directus/database \
  -v directus-uploads:/directus/uploads \
  -v directus-extensions:/directus/extensions \
  -e SECRET=replace-with-a-long-random-value \
  -e KEY=replace-with-a-second-long-random-value \
  -e ADMIN_EMAIL=admin@example.com \
  -e ADMIN_PASSWORD=replace-with-a-strong-bootstrap-password \
  -e DB_CLIENT=sqlite3 \
  -e DB_FILENAME=/directus/database/data.db \
  -e PUBLIC_URL=https://app.example.com \
  directus/directus:latest

If the process loops, compare the image's expected user with the owner of each mounted path. If it stays up, test port 8055 locally and then move directly to the workflow: bootstrap the administrator, create a collection and role, write through REST, query through GraphQL and upload a file. Version-pin the image only after that end-to-end check passes, and record the exact configuration beside the service.

Credentials, roles and exposed surfaces

Close the bootstrap window as soon as the first trusted administrator exists. Directus's concrete trap is using the bootstrap admin password after first login or rotating SECRET blindly; the safer boundary is to replace bootstrap credentials, use least-privilege roles and keep SECRET stable because it protects application sessions and tokens.

Generate SECRET once, keep it out of Git and preserve it with the recovery manifest because changing it can invalidate encrypted or signed application state. Private networking should carry dependency credentials, and roles inside Directus should grant the smallest useful action. Keep sensitive request bodies and provider responses out of routine logs.

Make the public origin unambiguous

Avoid temporary and permanent public origins for Directus. Instead, set PUBLIC_URL to the canonical HTTPS address, point the chosen DNS name at the platform route and proxy only to port 8055.

Exercise this action from outside the host: bootstrap the administrator, create a collection and role, write through REST, query through GraphQL and upload a file. If ingress fails, the 502 troubleshooting guide covers port and listener mistakes. If Directus receives the request but the database client is wrong or upload storage is not writable, the evidence now points beyond the proxy.

Failure drills for Directus

For Directus, monitor a transaction rather than a process: bootstrap the administrator, create a collection and role, write through REST, query through GraphQL and upload a file. Combine its latency and error rate with database connection pool, API request concurrency, Flow workers, thumbnail generation and upload storage so an alert identifies the constrained component.

The upgrade rehearsal must cover that Directus schema migrations, extensions and database vendor support must be checked as a unit. Restore, migrate and run the transaction before production replacement. If the database client is wrong or upload storage is not writable, do not erase data to make startup green; compare version, variables, mounts and dependency reachability in that order.

What Dockup should automate for Directus

The platform layer for Directus consists of port 8055, ingress, TLS, runtime configuration, storage and dependency reachability. Dockup can reproduce those pieces for its own infrastructure or a server the customer connects.

Then the operator finishes the product layer: set PUBLIC_URL to the canonical HTTPS address; enforce this access rule — replace bootstrap credentials, use least-privilege roles and keep SECRET stable because it protects application sessions and tokens; and run “bootstrap the administrator, create a collection and role, write through REST, query through GraphQL and upload a file”. Recording that test alongside the deployment avoids confusing automated provisioning with application readiness.

Frequently asked questions

What does Directus need for a production deployment?

Route the Directus container on port 8055 through one HTTPS origin. The supporting network requirement is Postgres plus optional Redis and object storage for scaled deployments. Do not call Directus ready until you can bootstrap the administrator, create a collection and role, write through REST, query through GraphQL and upload a file.

Which Directus data belongs in a backup?

Persist /directus/database and include database, uploads, extensions, flows and schema snapshots in the same recovery manifest. A clean Directus restore passes only when schema, roles, flows, items, extensions and uploads return and both REST and GraphQL probes succeed.

Does Directus require HTTPS behind a reverse proxy?

Use HTTPS for the public Directus origin and keep port 8055 on the internal route. Apply the Directus setting correctly: set PUBLIC_URL to the canonical HTTPS address. For Directus, HTTPS protects credentials or user content in transit and keeps origin-sensitive client behavior consistent.

How should a Directus upgrade be tested?

Restore current Directus state into an isolated deployment, apply the candidate version and repeat its acceptance transaction. Pay particular attention because Directus schema migrations, extensions and database vendor support must be checked as a unit. Keep the previous Directus image until its data-migration and rollback boundary are understood.