DEV Community

Cover image for Directus + Coolify: Should You Decouple Postgres & Redis?
Wade Thomas
Wade Thomas

Posted on

Directus + Coolify: Should You Decouple Postgres & Redis?

This is Part 2 of the Directus + Coolify series. If you're new here, start with "Secure Your VPS Before Hackers Do" and the first Directus + Coolify post — the bundled, single-Compose-file setup — before following along with this one.

Introduction

In the first method, we coupled all of the services into one stack using a single Docker Compose file. The network between all services was created automatically, and we didn't have to start them up individually — which removes the risk of a race condition if service startup isn't handled properly.

If you're running a single app, that's genuinely the recommended way to set up Directus on a Coolify-managed VPS. Going in, I assumed there were several good reasons to split the services apart instead — more control over backups, monitoring, restarts, that kind of thing. So before recommending decoupling, I actually tested each of those assumptions on a live Coolify instance.

Most of them turned out to be wrong.

Myth 1: Restarting Directus Restarts the Whole Stack

I expected that restarting Directus inside the bundled Compose file would restart Redis and Postgres along with it. It doesn't. Coolify lets you restart each service in the stack independently — Directus, Database, and Cache each get their own Restart button, right there in the same view. No decoupling needed for this one.

Myth 2: You Need a Separate Database Resource for S3 Backups

Same story. Even with Postgres bundled inside the Directus Compose file, Coolify still gives it its own dedicated Backups option, S3 included. This isn't a separate-resource-only feature.

Myth 3: Scheduled Tasks Require Separate Services

Also not true. Coolify exposes a Scheduled Tasks tab per service, even inside a single bundled stack — complete with a Container name dropdown letting you target the cron job at just the database, or just Directus, without splitting anything apart.

What Actually Holds Up

Two things survived testing.

First: metrics. This one's confirmed directly in Coolify's own documentation — CPU and memory metrics collection is explicitly not available for Docker Compose–based deployments. If you want to see per-container resource usage through Coolify's built-in monitoring, the service needs to be created as its own standalone resource, not bundled inside a Compose file. This is a real, documented limitation of the bundled approach.

Second: sharing a database across multiple apps. This one isn't a Coolify feature at all — it's just how Docker networking works. A database defined inside one app's Compose file lives on that stack's own private network by default. A second, completely separate application can't reach it without deliberately bridging the two networks. If you've got a desktop app and a mobile app that both need to talk to the same Postgres instance, that database needs to exist as its own standalone resource from the start — it can't stay tucked inside one app's Compose file.

So this really comes down to two reasons to decouple, not five — one a genuine Coolify limitation, the other a structural fact about Docker networking. Let's set both of those up properly.

Adding the Resources

Adding the PostgreSQL Database

  • Dashboard → Add Project
  • + Add Resource
  • Databases → PostgreSQL → Supabase PostgreSQL (with extensions)
  • Change the name to something human-friendly
  • Copy your username and password and save them somewhere — you'll need them shortly
  • Click Save
  • Click Start, and wait for the database to spin up (this can take a little while)

Once it's up, the status should read "Running (Healthy)."

💡 Enable metrics while you're here. In the sidebar, go to Servers → localhost → Metrics, and enable metrics. Back in your project, under Databases, click your Postgres database, then Metrics — you should now see live CPU/memory usage for it. This is the exact capability that isn't available on a bundled Compose deployment.

Adding Redis Cache

  • Dashboard → + Add Resource (from the project itself)
  • Databases → Redis
  • Rename it to something more convenient
  • Copy the Redis connection URL — you'll need it shortly
  • Save, then Start

Once both are running, SSH into your VPS and run:

docker ps
Enter fullscreen mode Exit fullscreen mode

to confirm both containers are up.

The Docker Compose Config for Directus

This is what goes into the empty Compose file for the Directus resource:

services:
  directus:
    image: 'directus/directus:12.2.0'
    ports:
      - '8055:8055'
    volumes:
      - './uploads:/directus/uploads'
      - './extensions:/directus/extensions'
    healthcheck:
      test:
        - CMD-SHELL
        - 'wget --spider -q http://127.0.0.1:8055/server/ping || exit 1'
      interval: 10s
      timeout: 5s
      retries: 5
      start_interval: 5s
      start_period: 30s
    environment:
      SECRET: secretstring
      MARKETPLACE_TRUST: all
      DB_CLIENT: pg
      DB_HOST:
      DB_PORT: '5432'
      DB_DATABASE: postgres
      DB_USER: postgres
      DB_PASSWORD:
      CACHE_ENABLED: 'true'
      CACHE_STORE: redis
      CACHE_AUTO_PURGE: 'true'
      REDIS:
      ADMIN_EMAIL: joepublic@example.com
      ADMIN_PASSWORD: '1234567890'
      CORS_ENABLED: 'true'
      CORS_ORIGIN: 'true'
      CORS_CREDENTIALS: 'true'
      PUBLIC_URL:
Enter fullscreen mode Exit fullscreen mode

Notice the healthcheck already uses 127.0.0.1 instead of localhost — that's the fix from Part 1. Carrying it forward here saves you from hitting the exact same "unhealthy" bug all over again.

Adding Directus

  • Dashboard → + Add Resource (from the project itself)
  • Applications → Docker Compose Empty
  • Paste in the Compose configuration above
  • Click Save
  • Network → Connect To Predefined Network → check the box
  • Services → Directus service → Settings
  • Add your Directus subdomain — remember to use https:// (e.g. https://directus.yourdomain.com)
  • Save

Connecting the Services Together

Variable What it is
SECRET A long, unguessable random string
DB_HOST The name of your Postgres container — run docker ps on your VPS to find it
DB_DATABASE The name of the database on your Postgres server (Coolify's default is usually postgres, but confirm it against your Postgres service)
DB_USER The database username, from the Postgres service you created earlier
DB_PASSWORD The password from that same Postgres service
REDIS The Redis connection URL from the Redis service you created earlier
PUBLIC_URL Your Directus subdomain

⚠️ Redis URL gotcha: the connection URL follows the format redis://username:password@host:port. Coolify's generated URL includes the username you set when creating the Redis resource — in my case, that username was also redis, so the URL looked like redis://redis:somelongvariable.... Directus doesn't need the username here, just the password, so strip that segment out: redis://:somelongvariable.... If you used a different username when creating your Redis resource, remove that value instead — not literally the word "redis."

Setting Up Environment Variables

Rather than hardcoding any of this directly into the Compose file, move it into Directus's environment variables:

  • Dashboard → Projects → Services / Directus
  • Environment Variables
  • Click + Add, enter the variable name in all caps, and its value
  • Save
  • Repeat for: SECRET, DB_HOST, DB_DATABASE, DB_USER, DB_PASSWORD, ADMIN_EMAIL, ADMIN_PASSWORD, and REDIS

Updating the Compose File

Now reference those variables instead of the raw values:

SECRET: '${SECRET}'
DB_HOST: '${DB_HOST}'
Enter fullscreen mode Exit fullscreen mode

...and so on for each variable. Then:

  • Save
  • Restart

Launching Directus

Paste your Directus subdomain into the browser, and you should land on your running Directus instance. You can also click Links on the Directus service, then click the subdomain — it'll take you straight to your Directus Studio login.

Same as the last video: sign up for your free license, which arrives by email, and paste it into your Directus instance to unlock everything.


Any questions or hit a different result testing any of these yourself? Drop it in the comments — I'm genuinely curious whether this holds up across different Coolify versions and setups.

Top comments (0)