Back to Article List

Deploy Docker Compose apps on Coolify

Deploy Docker Compose apps on Coolify

Coolify Docker Compose support is the feature people use without realizing it's there. Every one-click service in the catalog, n8n, Strapi, Plausible, Supabase, Ghost, all of them, is just a Docker Compose file under the hood with a Coolify-specific extension layer that handles environment variables, persistent storage and SSL. Once you understand how Coolify reads Compose files, you can deploy any containerized application that ships with a working docker-compose.yml.

This guide covers the full surface. The two Compose modes Coolify supports, the magic environment variables that auto-generate URLs and passwords, the is_directory and content annotations that handle file-based configuration, multi-container apps and the limits to know about. Written against Coolify v4.0.0.

If you're new to Coolify, the LumaDock Coolify VPS ships with v4 pre-installed and the getting started guide covers initial dashboard access.

Two Coolify Docker Compose modes

Coolify v4 supports two ways to deploy a Docker Compose stack:

Docker Compose Build Pack

Used when your Compose file lives in a Git repository. You deploy it like any other application, Coolify clones the repo, reads the docker-compose.yml at the root (or wherever you configure), builds any services that have build: directives and starts the stack. This is the path you want for applications you're developing yourself with a Compose file in source control.

Coolify's documentation calls this the "Docker Compose Build Pack" and it's available as a build pack option when you add an application from a Git source. Pick it explicitly if Coolify auto-detected something else or leave it on auto-detection if your repo has a docker-compose.yml at the root.

Service Stack (paste a Compose file directly)

Used when you want to deploy a stack from a Compose file you have on hand without putting it in a Git repo. From a Coolify project, click Add a new resource, then pick Service, then paste your Compose file directly into the editor. Coolify saves it, builds and starts the stack.

This is how the one-click services catalog works internally. Each "one-click" is a curated Compose file that Coolify maintains, you click the service name and Coolify pastes the file for you, you tweak env vars, deploy. You can do the same with any Compose file you have, off-the-shelf or custom.

Magic environment variables

This is the feature that turns "raw Compose file" into "Coolify Compose deployment." Coolify recognizes a special syntax in your Compose file that auto-generates values for environment variables: URLs, fully qualified domain names, passwords and random strings. The values stay consistent across all services in the stack so they reference each other correctly.

The syntax is SERVICE_<TYPE>_<IDENTIFIER>. The supported types include:

  • SERVICE_FQDN_*, generates a fully qualified domain name for the service. Coolify allocates a domain on your wildcard subdomain (or you specify one)
  • SERVICE_URL_*, generates a full URL including protocol
  • SERVICE_PASSWORD_*, generates a random password
  • SERVICE_USER_*, generates a random username
  • SERVICE_BASE64_*, generates a random base64 string
  • SERVICE_REALBASE64_*, generates a real base64-encoded string

An example. Suppose you have a Compose file with two services, an app and a Postgres database and you want them to share a database password without you having to set it manually:

services:
  app:
    image: my-app:latest
    environment:
      - DATABASE_URL=postgres://app:${SERVICE_PASSWORD_DB}@db:5432/myapp
      - APP_URL=${SERVICE_FQDN_APP}
  db:
    image: postgres:16
    environment:
      - POSTGRES_USER=app
      - POSTGRES_PASSWORD=${SERVICE_PASSWORD_DB}
      - POSTGRES_DB=myapp

Coolify generates a random password for SERVICE_PASSWORD_DB on first deploy, uses the same value in both places (so the connection works) and persists it across redeploys. SERVICE_FQDN_APP becomes a domain like app-xyz.your-coolify-domain.com automatically.

This feature requires Coolify v4.0.0-beta.411 or newer for Compose-from-Git deployments, which includes the current stable v4.0.0. If you're on an older version, magic env vars work in Service Stack mode but not in the Compose Build Pack from Git.

Coolify-specific Compose annotations

Beyond magic env vars, Coolify extends the standard Compose syntax with two annotations that solve common pain points.

The is_directory annotation

Standard Docker volumes assume the path you mount already exists. If it doesn't, Docker creates a file at that path (not a directory), which is rarely what you want for application data. Coolify's is_directory: true annotation tells Coolify to create a directory at the host path before mounting:

services:
  filebrowser:
    image: filebrowser/filebrowser:latest
    volumes:
      - type: bind
        source: ./srv
        target: /srv
        is_directory: true

Coolify creates ./srv as a directory on the host before the container starts, ensuring the bind mount works as expected. Without this, you'd get a file mount or a startup error. This annotation is Coolify-specific and won't work in plain Docker Compose outside Coolify, your file is fine to use either way because Docker ignores unknown annotations.

The content annotation

Sometimes you want a small config file inside the container, baked into the deployment, without committing the file to source control or maintaining it as a separate artifact. The content: annotation lets you specify file contents inline:

services:
  postgres:
    image: postgres:16
    environment:
      - POSTGRES_PASSWORD=${SERVICE_PASSWORD_DB}
    volumes:
      - type: bind
        source: ./init.sql
        target: /docker-entrypoint-initdb.d/init.sql
        content: |
          CREATE TABLE users (id SERIAL PRIMARY KEY, name VARCHAR(255));
          INSERT INTO users (name) VALUES ('admin');

Coolify creates the file with the inline content at deploy time, then mounts it into the container. The $VARIABLE syntax inside the content gets substituted at deploy time too, which means you can interpolate magic env vars and other dynamic values into the file content.

This is most useful for database init scripts, small config files for sidecars and any one-off file an image needs that you'd rather not maintain externally.

Deploy a multi-container app from Git

Walking through the full flow with a real example. Suppose you have a Next.js app with a Postgres database, both in the same repo, with a docker-compose.yml at the root. The Compose file looks something like:

services:
  web:
    build: .
    environment:
      - DATABASE_URL=postgres://app:${SERVICE_PASSWORD_DB}@db:5432/myapp
      - NEXTAUTH_URL=${SERVICE_FQDN_WEB}
    depends_on:
      - db
    
  db:
    image: postgres:16
    environment:
      - POSTGRES_USER=app
      - POSTGRES_PASSWORD=${SERVICE_PASSWORD_DB}
      - POSTGRES_DB=myapp
    volumes:
      - type: volume
        source: postgres-data
        target: /var/lib/postgresql/data

volumes:
  postgres-data:

Steps in Coolify:

  1. Create a project, click Add a new resource → Private Repository (with GitHub App)
  2. Pick the repo and branch, Coolify scans
  3. For build pack, select Docker Compose
  4. Coolify reads the Compose file, shows the services it'll deploy
  5. Coolify auto-generates values for SERVICE_PASSWORD_DB and SERVICE_FQDN_WEB on first deploy, you can override in the Environment Variables tab if you want specific values
  6. Click Deploy

Coolify builds the web service from the Dockerfile in the repo (because it has build: .), pulls the postgres:16 image, sets up the persistent volume for Postgres, starts both services. Traefik picks up the web service via the auto-generated FQDN and routes external traffic.

How Coolify handles common Compose features

depends_on

Coolify respects the depends_on directive. Services start in the order their dependencies require. If you also use healthcheck, Coolify waits for the dependency's health check to pass before starting dependent services, which is more robust than just "start in order."

Networks

By default, all services in a Coolify Compose deployment share a single Docker network so they can reach each other by service name. You can define custom networks in your Compose file if you need finer separation, Coolify respects them. The network setup includes a connection to the Coolify proxy network so Traefik can route traffic, you don't need to configure that explicitly.

Volumes

Both named volumes (managed by Docker) and bind mounts (paths on the host) work. For persistent data you want surviving deploys, use named volumes. Coolify also has a separate "Storages" UI for the application-level resources outside Compose mode, in Compose mode you stick with the standard syntax.

Environment variables

Three layers, in order of precedence:

  1. Variables defined inline in the Compose file (lowest precedence)
  2. Variables in Coolify's Environment Variables tab (overrides inline ones)
  3. Magic env vars (SERVICE_*) auto-generated by Coolify (overrides both)

For sensitive values you don't want in your repo, define them in Coolify's UI. They're encrypted at rest and won't end up in your Git history.

Build directive

Yes, build: in the Compose file works. Coolify clones the repo, runs the build step, tags the image and uses it in the deployed stack. You can use Dockerfile inline references, build contexts, build args, all the standard Compose build features.

Limits and gotchas

Some Compose features don't quite work the way you'd expect.

Service replicas in Compose mode

The deploy.replicas directive (Compose 3.x) is honored, but Docker Compose's standalone replicas don't give you load balancing the way Docker Swarm does. For multiple instances of a service with proper load balancing, you'd want either Docker Swarm mode (which Coolify doesn't deeply integrate with for application replicas) or external load balancing.

Restart policies

The restart: directive works. Coolify's default for new applications is unless-stopped if you don't specify, which means the container survives a server reboot. Match this in your Compose file or set it explicitly.

External secrets

Compose has a secrets: directive for binding host files into containers as secrets. This works in Coolify but the file paths need to exist on the Coolify host. The content: annotation we covered above is usually a cleaner alternative for inline secret content.

Profiles

Compose profiles (services that only start under specific profile names) work, but Coolify doesn't have a UI for selecting profiles per deploy. If you need profile-based deploys, define separate Compose files for each profile and switch between them.

Resource limits

Memory and CPU limits via deploy.resources.limits are honored. Useful for preventing one runaway container from OOMing your whole server. Set them on services that have known unbounded growth (build processes, large query workloads).

Switching from a Dockerfile build to a Compose build

If you initially deployed your app with the Nixpacks or Dockerfile build pack and want to switch to Compose, the cleanest path is to delete the old application resource and create a new one with the Compose build pack. Coolify doesn't have a "switch build pack" feature on an existing application, the build pack is set at creation.

Before deleting, copy your environment variables, custom domains, persistent storage paths and any other configuration. Recreate them on the new application with Compose. The downtime during the switch is the time it takes to do the initial deploy on the new app, usually a few minutes.

Common Coolify Docker Compose problems and how to fix them

Compose file not detected from Git repo

By default, Coolify looks for docker-compose.yml at the repo root. If yours is at a different path (subdirectory of a monorepo or named compose.yml), set the path explicitly in the application's General tab under "Docker Compose Location." For monorepos, set both the path AND the Base Directory so the build context is right.

Build directive fails with "context not found"

Your Compose file has a build: ./subdir but Coolify is cloning the repo into a different working directory. Use build: with an explicit context: field set to a path relative to the Compose file's location, not absolute paths.

Magic env vars show as literal strings

You're on an old Coolify version or the syntax is slightly off. Magic env vars require Coolify v4.0.0-beta.411+ for Compose Build Pack. The syntax has to match exactly, SERVICE_PASSWORD_ followed by your identifier with no spaces, no special characters except underscore. If you wrote ${SERVICE_PASSWORD-DB} with a hyphen, Coolify won't recognize it.

Database starts but app can't connect

The hostname in your Compose file should be the service name as defined in the same Compose file (e.g., db in our example), not localhost or the resource name Coolify shows. Inside a single Compose stack, services reach each other by service name on the internal network.

Persistent volume gets wiped on redeploy

You used a bind mount with a path that gets cleared during deploy, instead of a named volume. Switch to a named volume (defined in the volumes: top-level section) for any data that needs to survive deploys. Bind mounts are fine for read-only config files but not for application state.

Port conflicts when adding a second Compose stack

Two Compose stacks on the same Coolify host can't both publish the same host port. If both have ports: ["80:80"], the second deploy fails. Use Traefik routing (don't publish ports directly, let Traefik handle 80/443) or pick different host ports for each stack.

When NOT to use Coolify Docker Compose

The Compose build pack isn't always the right choice. For single-service Node.js, Python or Ruby apps, the Nixpacks build pack is faster, simpler and gives you build caching that Compose doesn't. Use Compose mode when you specifically need:

  • Multiple services that start and stop together (app + database + cache + worker)
  • A specific Compose feature like depends_on conditions or healthcheck-aware ordering
  • An off-the-shelf application that ships with a Compose file (Strapi, Ghost, Plausible, n8n, etc.)
  • A custom build environment that's easier to express in a Dockerfile + Compose than in Nixpacks

For a typical Next.js or Laravel app where you just want to push and deploy, Nixpacks is the better default. The Next.js on Coolify guide covers Nixpacks vs Dockerfile in detail and the Laravel on Coolify guide covers the equivalent for PHP.

This is what I suggest you do next:

Once you have multi-container apps deploying through Compose, the natural next steps are SSL configuration for your domain (the Coolify SSL guide covers Let's Encrypt and custom certs), monitoring the running stack (the Coolify monitoring guide covers Prometheus and Grafana setup) and automated database backups for any database services in your stack (the Coolify backups guide covers Postgres, MySQL and MongoDB to S3).

Stop installing. Start shipping.

LumaDock Coolify plans come with the dashboard pre-installed, unmetered bandwidth and a flat monthly bill. Try the server risk free with a 30-day refund guarantee.

Your idea deserves better hosting

24/7 support 30-day money-back guarantee Cancel anytime
Ciclo de Facturación

1 GB RAM VPS

$3.99 Save  25 %
$2.99 Mensual
  • 1 vCPU AMD EPYC
  • 30 GB NVMe disco
  • Ilimitado ancho de banda
  • IPv4 e IPv6 incluidos El soporte IPv6 no está disponible en Francia, Finlandia o Países Bajos.
  • 1 Gbps red
  • Gestión de firewall
  • Monitoreo gratis

2 GB RAM VPS

$5.99 Save  17 %
$4.99 Mensual
  • 2 vCPU AMD EPYC
  • 30 GB NVMe disco
  • Ilimitado ancho de banda
  • IPv4 e IPv6 incluidos El soporte IPv6 no está disponible en Francia, Finlandia o Países Bajos.
  • 1 Gbps red
  • Gestión de firewall
  • Monitoreo gratis

6 GB RAM VPS

$14.99 Save  33 %
$9.99 Mensual
  • 6 vCPU AMD EPYC
  • 70 GB NVMe disco
  • Ilimitado ancho de banda
  • IPv4 e IPv6 incluidos El soporte IPv6 no está disponible en Francia, Finlandia o Países Bajos.
  • 1 Gbps red
  • Gestión de firewall
  • Monitoreo gratis

AMD EPYC VPS.P1

$7.99 Save  25 %
$5.99 Mensual
  • 2 vCPU AMD EPYC
  • 4 GB memoria RAM
  • 40 GB NVMe disco
  • Ilimitado ancho de banda
  • IPv4 e IPv6 incluidos El soporte IPv6 no está disponible en Francia, Finlandia o Países Bajos.
  • 1 Gbps red
  • Copia automática incluida
  • Gestión de firewall
  • Monitoreo gratis

AMD EPYC VPS.P2

$14.99 Save  27 %
$10.99 Mensual
  • 2 vCPU AMD EPYC
  • 8 GB memoria RAM
  • 80 GB NVMe disco
  • Ilimitado ancho de banda
  • IPv4 e IPv6 incluidos El soporte IPv6 no está disponible en Francia, Finlandia o Países Bajos.
  • 1 Gbps red
  • Copia automática incluida
  • Gestión de firewall
  • Monitoreo gratis

AMD EPYC VPS.P4

$29.99 Save  20 %
$23.99 Mensual
  • 4 vCPU AMD EPYC
  • 16 GB memoria RAM
  • 160 GB NVMe disco
  • Ilimitado ancho de banda
  • IPv4 e IPv6 incluidos El soporte IPv6 no está disponible en Francia, Finlandia o Países Bajos.
  • 1 Gbps red
  • Copia automática incluida
  • Gestión de firewall
  • Monitoreo gratis

AMD EPYC VPS.P5

$36.49 Save  21 %
$28.99 Mensual
  • 8 vCPU AMD EPYC
  • 16 GB memoria RAM
  • 180 GB NVMe disco
  • Ilimitado ancho de banda
  • IPv4 e IPv6 incluidos El soporte IPv6 no está disponible en Francia, Finlandia o Países Bajos.
  • 1 Gbps red
  • Copia automática incluida
  • Gestión de firewall
  • Monitoreo gratis

AMD EPYC VPS.P6

$56.99 Save  21 %
$44.99 Mensual
  • 8 vCPU AMD EPYC
  • 32 GB memoria RAM
  • 200 GB NVMe disco
  • Ilimitado ancho de banda
  • IPv4 e IPv6 incluidos El soporte IPv6 no está disponible en Francia, Finlandia o Países Bajos.
  • 1 Gbps red
  • Copia automática incluida
  • Gestión de firewall
  • Monitoreo gratis

AMD EPYC VPS.P7

$69.99 Save  20 %
$55.99 Mensual
  • 16 vCPU AMD EPYC
  • 32 GB memoria RAM
  • 240 GB NVMe disco
  • Ilimitado ancho de banda
  • IPv4 e IPv6 incluidos El soporte IPv6 no está disponible en Francia, Finlandia o Países Bajos.
  • 1 Gbps red
  • Copia automática incluida
  • Gestión de firewall
  • Monitoreo gratis

EPYC Genoa VPS.G1

$4.99 Save  20 %
$3.99 Mensual
  • 1 vCPU AMD EPYC Gen4 AMD EPYC Genoa de 4ª generación 9xx4 con 3.25 GHz o similar, basado en la arquitectura Zen 4.
  • 1 GB DDR5 memoria RAM
  • 25 GB NVMe disco
  • Ilimitado ancho de banda
  • IPv4 e IPv6 incluidos El soporte IPv6 no está disponible en Francia, Finlandia o Países Bajos.
  • 1 Gbps red
  • Copia automática incluida
  • Gestión de firewall
  • Monitoreo gratis

EPYC Genoa VPS.G2

$12.99 Save  23 %
$9.99 Mensual
  • 2 vCPU AMD EPYC Gen4 AMD EPYC Genoa de 4ª generación 9xx4 con 3.25 GHz o similar, basado en la arquitectura Zen 4.
  • 4 GB DDR5 memoria RAM
  • 50 GB NVMe disco
  • Ilimitado ancho de banda
  • IPv4 e IPv6 incluidos El soporte IPv6 no está disponible en Francia, Finlandia o Países Bajos.
  • 1 Gbps red
  • Copia automática incluida
  • Gestión de firewall
  • Monitoreo gratis

EPYC Genoa VPS.G4

$25.99 Save  27 %
$18.99 Mensual
  • 4 vCPU AMD EPYC Gen4 AMD EPYC Genoa de 4ª generación 9xx4 con 3.25 GHz o similar, basado en la arquitectura Zen 4.
  • 8 GB DDR5 memoria RAM
  • 100 GB NVMe disco
  • Ilimitado ancho de banda
  • IPv4 e IPv6 incluidos El soporte IPv6 no está disponible en Francia, Finlandia o Países Bajos.
  • 1 Gbps red
  • Copia automática incluida
  • Gestión de firewall
  • Monitoreo gratis

EPYC Genoa VPS.G5

$44.99 Save  33 %
$29.99 Mensual
  • 4 vCPU AMD EPYC Gen4 AMD EPYC Genoa de 4ª generación 9xx4 con 3.25 GHz o similar, basado en la arquitectura Zen 4.
  • 16 GB DDR5 memoria RAM
  • 150 GB NVMe disco
  • Ilimitado ancho de banda
  • IPv4 e IPv6 incluidos El soporte IPv6 no está disponible en Francia, Finlandia o Países Bajos.
  • 1 Gbps red
  • Copia automática incluida
  • Gestión de firewall
  • Monitoreo gratis

EPYC Genoa VPS.G6

$48.99 Save  31 %
$33.99 Mensual
  • 8 vCPU AMD EPYC Gen4 AMD EPYC Genoa de 4ª generación 9xx4 con 3.25 GHz o similar, basado en la arquitectura Zen 4.
  • 16 GB DDR5 memoria RAM
  • 200 GB NVMe disco
  • Ilimitado ancho de banda
  • IPv4 e IPv6 incluidos El soporte IPv6 no está disponible en Francia, Finlandia o Países Bajos.
  • 1 Gbps red
  • Copia automática incluida
  • Gestión de firewall
  • Monitoreo gratis

EPYC Genoa VPS.G7

$74.99 Save  27 %
$54.99 Mensual
  • 8 vCPU AMD EPYC Gen4 AMD EPYC Genoa de 4ª generación 9xx4 con 3.25 GHz o similar, basado en la arquitectura Zen 4.
  • 32 GB DDR5 memoria RAM
  • 250 GB NVMe disco
  • Ilimitado ancho de banda
  • IPv4 e IPv6 incluidos El soporte IPv6 no está disponible en Francia, Finlandia o Países Bajos.
  • 1 Gbps red
  • Copia automática incluida
  • Gestión de firewall
  • Monitoreo gratis

GPU products are in high demand at the moment. Fill the form to get notified as soon as your preferred GPU server is back in stock.