diff --git a/COOLIFY_DOCKER_COMPOSE_KNOWLEDGE.md b/COOLIFY_DOCKER_COMPOSE_KNOWLEDGE.md new file mode 100644 index 0000000..6c6d20e --- /dev/null +++ b/COOLIFY_DOCKER_COMPOSE_KNOWLEDGE.md @@ -0,0 +1,324 @@ +# Coolify Docker Compose Deployment Knowledge + +Source: +- Local archive: `viewpagesourcedockercomposecoolify.html` +- Official page: https://coolify.io/docs/applications/build-packs/docker-compose +- Cross-check: Coolify docs via Context7, `/coollabsio/coolify-docs` +- Last reviewed: 2026-06-11 + +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. + +## Recommended Compose Rules + +- 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: + +```yaml +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: + +```yaml +# 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: + +```yaml +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__` pattern. They can generate URLs, FQDNs, passwords, users, base64 strings, and hex strings. + +Examples: + +```yaml +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: + +```text +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: + +```yaml +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: + +```yaml +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: + +```yaml +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: + +```yaml +services: + app: + depends_on: + db: + condition: service_healthy +``` + +For one-time jobs or migration-only services, Coolify supports excluding a service from overall healthchecks: + +```yaml +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 + +```yaml +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.