Files
WebGIS_Jalan-JalanRusak/COOLIFY_DOCKER_COMPOSE_KNOWLEDGE.md
T

9.9 KiB

Coolify Docker Compose Deployment Knowledge

Source:

This note summarizes how to prepare a project that uses Docker Compose for deployment in Coolify. Use it as a checklist for other projects before deploying.

Core Model

Coolify can deploy a project from a docker-compose.yml or docker-compose.yaml file. For Docker Compose deployments, the compose file becomes the main source of truth for services, build settings, environment variables, volumes, healthchecks, and service networking.

Coolify also attaches its reverse proxy to the stack network so public traffic can reach the exposed application service.

  • Keep service names stable because services communicate by service name, for example DB_HOST=db.
  • Do not use localhost or 127.0.0.1 to connect from one container to another container.
  • Prefer expose for internal container ports and let Coolify handle public routing through its proxy.
  • Use ports only when you intentionally need host-level port binding.
  • Add restart: unless-stopped for long-running services.
  • Add healthchecks for services that must be healthy before the stack is considered ready.
  • Avoid hardcoded production secrets in the compose file.
  • Put persistent data in named volumes.
  • Do not define custom networks: unless you have a specific, tested reason.

Networking

Coolify automatically creates an isolated bridge network for each Compose stack. All services in the same stack can reach each other by service name.

Example:

services:
  app:
    environment:
      DB_HOST: db

  db:
    image: mysql:8.0

The app container connects to MySQL using db:3306, not localhost:3306.

Do Not Define Custom Networks

For normal Coolify Compose deployments, remove custom networks:

# Avoid this in Coolify unless you know exactly why it is needed.
services:
  frontend:
    networks:
      - my-network
  backend:
    networks:
      - my-network

networks:
  my-network:
    driver: bridge

Why: Coolify already creates a managed network and connects Traefik to it. Extra custom networks can cause intermittent routing problems because the proxy may select an unreachable container IP. Symptoms include hanging requests and 504 Gateway Timeout.

Use the auto-created network unless you need cross-stack communication. For cross-stack communication, use Coolify's predefined network option and reference the full remote service name when required.

Environment Variables

Coolify parses compose environment variables and exposes many of them in its UI.

Common forms:

environment:
  APP_ENV: production
  APP_DEBUG: "${APP_DEBUG:-false}"
  APP_KEY: "${APP_KEY:?Set APP_KEY in Coolify}"
  DB_PASSWORD: "${DB_PASSWORD:?Set DB_PASSWORD in Coolify}"

Meaning:

  • VALUE: hardcoded into the container.
  • ${VAR}: comes from Coolify/project environment.
  • ${VAR:-default}: uses default if unset.
  • ${VAR:?message}: required; deployment should fail early if unset.

For production, use required variables for secrets so a deploy cannot silently start with weak defaults.

Magic Environment Variables

Coolify supports magic variables with the SERVICE_<TYPE>_<IDENTIFIER> pattern. They can generate URLs, FQDNs, passwords, users, base64 strings, and hex strings.

Examples:

environment:
  APP_URL: "${SERVICE_URL_APP_80}"
  DB_PASSWORD: "${SERVICE_PASSWORD_64_MYSQL}"

Common types:

  • SERVICE_URL_*
  • SERVICE_FQDN_*
  • SERVICE_PASSWORD_*
  • SERVICE_PASSWORD_64_*
  • SERVICE_PASSWORDWITHSYMBOLS_64_*
  • SERVICE_BASE64_*
  • SERVICE_HEX_*

Use these only when you intentionally want Coolify to create and own that variable.

Important pitfall: if a magic variable is declared in docker-compose.yaml, Coolify may block deletion from the UI with a message like:

Cannot delete environment variable 'SERVICE_FQDN_APP'
Please remove it from the Docker Compose file first.

Fix:

  1. Remove the related SERVICE_FQDN_* or SERVICE_URL_* entry from docker-compose.yaml.
  2. Redeploy or resync the resource in Coolify.
  3. Delete the variable again from the Coolify UI.

Domains and Public Ports

For web apps, Coolify needs to know which service and port should receive public traffic.

Recommended project-side setup:

services:
  app:
    expose:
      - "80"

Then configure the domain/proxy in Coolify to target service app on port 80.

Do not add SERVICE_URL_APP_80 just to expose the app if you prefer managing domains in the Coolify UI. Declaring SERVICE_URL_* makes Coolify manage related magic variables.

Volumes and Persistent Data

Use named volumes for database and persistent application data:

services:
  db:
    image: mysql:8.0
    volumes:
      - mysql-data:/var/lib/mysql

volumes:
  mysql-data:

For application uploads, logs, or generated files, map only the directories that must persist. Do not persist build output unless the app requires runtime writes there.

Healthchecks

Use healthchecks for databases and web services:

services:
  app:
    healthcheck:
      test: ["CMD-SHELL", "curl -fsS http://127.0.0.1/ >/dev/null || exit 1"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 30s

  db:
    healthcheck:
      test: ["CMD-SHELL", "mysqladmin ping -h localhost -uroot -p\"$${MYSQL_ROOT_PASSWORD}\" --silent"]
      interval: 5s
      timeout: 5s
      retries: 20
      start_period: 30s

Use depends_on with condition: service_healthy when the app must wait for the database:

services:
  app:
    depends_on:
      db:
        condition: service_healthy

For one-time jobs or migration-only services, Coolify supports excluding a service from overall healthchecks:

services:
  migrate:
    exclude_from_hc: true

Laravel Notes

For Laravel projects:

  • Use a real web server in production, such as Apache or Nginx with PHP-FPM.
  • Do not use php artisan serve as the production command.
  • Set APP_ENV=production.
  • Set APP_DEBUG=false.
  • Set APP_KEY in Coolify.
  • Set APP_URL to the final public URL.
  • Use DB_HOST as the Compose service name, usually db.
  • Run migrations intentionally during entrypoint/deploy, and ensure seeders are idempotent if they run on every deploy.
  • Keep .env out of Git; define production values in Coolify environment variables.

MySQL Password Warning

MySQL initializes credentials only when the database volume is first created. If you change MYSQL_PASSWORD, MYSQL_ROOT_PASSWORD, or DB_PASSWORD after the volume already exists, the existing database users may still use the old password.

If the app cannot connect after changing passwords:

  • Set DB_PASSWORD back to the password used when the volume was initialized, or
  • Rotate the database password manually inside MySQL, or
  • Recreate the database volume if data can be deleted.

Minimal Production Template

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    restart: unless-stopped
    expose:
      - "80"
    environment:
      APP_ENV: "${APP_ENV:-production}"
      APP_DEBUG: "${APP_DEBUG:-false}"
      APP_URL: "${APP_URL:?Set APP_URL in Coolify}"
      APP_KEY: "${APP_KEY:?Set APP_KEY in Coolify}"
      DB_CONNECTION: mysql
      DB_HOST: "${DB_HOST:-db}"
      DB_PORT: "${DB_PORT:-3306}"
      DB_DATABASE: "${DB_DATABASE:-app}"
      DB_USERNAME: "${DB_USERNAME:-app}"
      DB_PASSWORD: "${DB_PASSWORD:?Set DB_PASSWORD in Coolify}"
    depends_on:
      db:
        condition: service_healthy
    healthcheck:
      test: ["CMD-SHELL", "curl -fsS http://127.0.0.1/ >/dev/null || exit 1"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 30s

  db:
    image: mysql:8.0
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: "${DB_DATABASE:-app}"
      MYSQL_USER: "${DB_USERNAME:-app}"
      MYSQL_PASSWORD: "${DB_PASSWORD:?Set DB_PASSWORD in Coolify}"
      MYSQL_ROOT_PASSWORD: "${DB_ROOT_PASSWORD:?Set DB_ROOT_PASSWORD in Coolify}"
    volumes:
      - mysql-data:/var/lib/mysql
    healthcheck:
      test: ["CMD-SHELL", "mysqladmin ping -h localhost -uroot -p\"$${MYSQL_ROOT_PASSWORD}\" --silent"]
      interval: 5s
      timeout: 5s
      retries: 20
      start_period: 30s

volumes:
  mysql-data:

Pre-Deploy Checklist

  • docker compose config --quiet passes locally.
  • docker compose build app passes locally or in CI.
  • App image exposes the same port configured in Coolify.
  • No custom networks: are defined for normal stacks.
  • Public app service has a healthcheck.
  • Database service has a healthcheck.
  • App uses database service hostname, not localhost.
  • Required secrets are set in Coolify.
  • No weak fallback secrets remain for production.
  • Persistent data is stored in named volumes.
  • Magic SERVICE_* variables are only present when intentionally managed by Coolify.

Troubleshooting

Cannot delete SERVICE_FQDN_APP

Cause: a related magic variable is still declared in compose, commonly SERVICE_FQDN_APP, SERVICE_URL_APP, or SERVICE_URL_APP_80.

Fix: remove it from compose, redeploy/resync, then delete it in Coolify.

504 Gateway Timeout

Check:

  • Remove custom networks.
  • Confirm Coolify routes to the correct service and port.
  • Confirm the app listens on 0.0.0.0, not only 127.0.0.1.
  • Check app healthcheck and container logs.

App cannot connect to database

Check:

  • DB_HOST is the database service name, for example db.
  • DB_PORT=3306.
  • DB_DATABASE, DB_USERNAME, and DB_PASSWORD match the database service.
  • Existing MySQL volume may still use an old password.

Environment value keeps coming back

Check whether that variable is declared in docker-compose.yaml. Coolify treats compose-declared variables as managed by the compose file.