| name | coolify-compose |
| description | Convert Docker Compose files to Coolify templates. Use when creating Coolify services, converting docker-compose.yml for Coolify deployment, working with SERVICE_URL/SERVICE_PASSWORD magic variables, or troubleshooting Coolify compose errors. |
Coolify Docker Compose
Convert standard Docker Compose files into Coolify-compatible templates with automatic credential generation, dynamic URLs, and one-click deployment.
Two Deployment Modes
Coolify supports two ways to deploy compose files with different capabilities:
1. Raw Compose (Paste Content)
Paste compose YAML directly into Coolify's UI. Limited feature set:
| Feature | Supported |
|---|
image: | ✅ Yes |
build: | ❌ No - must use pre-built images |
| External config files | ❌ No - must use inline content: |
YAML anchors (&, *) | ✅ Yes - resolved by YAML parser |
| Coolify magic variables | ✅ Yes |
content: for inline files | ✅ Yes |
Use when: Quick deployments, simple services, no custom images needed.
2. Repository Mode (Git URL)
Point Coolify to a Git repository containing your compose file. Full Docker Compose features:
| Feature | Supported |
|---|
image: | ✅ Yes |
build: | ✅ Yes - builds from Dockerfile in repo |
| External config files | ✅ Yes - relative paths work |
| Coolify magic variables | ✅ Yes |
content: for inline files | ✅ Yes |
Use when: Custom images needed, complex multi-file setups, existing docker-compose.yml in a repo.
Repository setup:
my-service/
├── compose.yml # or docker-compose.yml
├── custom-image/
│ ├── Dockerfile
│ └── config.sql
└── other-files/
services:
app:
build:
context: ./custom-image
dockerfile: Dockerfile
Which Mode to Use?
| Original compose has... | Recommended mode |
|---|
Only image: references | Either works |
build: directives | Repository mode |
| External config files to mount | Repository mode (or use content: in raw) |
| Single simple service | Raw mode is faster |
Quick Start
Every Coolify template needs a header and magic variables:
services:
app:
image: myapp:latest
environment:
- SERVICE_URL_APP_3000
- DATABASE_URL=postgres://${SERVICE_USER_POSTGRES}:${SERVICE_PASSWORD_POSTGRES}@db:5432/mydb
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/health"]
interval: 5s
timeout: 10s
retries: 10
db:
image: postgres:16-alpine
environment:
- POSTGRES_USER=$SERVICE_USER_POSTGRES
- POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER}"]
interval: 5s
timeout: 10s
retries: 10
Conversion Checklist
When converting a standard docker-compose.yml:
0. Check for build: Directives
If the compose has build: entries:
- Repository mode: Keep them. Coolify will build from the Dockerfile.
- Raw mode: Replace with pre-built
image: references or find equivalent images.
services:
custom-db:
build: ./custom-postgres
custom-db:
image: your-registry.com/custom-postgres:latest
custom-db:
build:
context: ./custom-postgres
dockerfile: Dockerfile
1. Add Header Metadata
2. Replace Hardcoded Credentials
POSTGRES_PASSWORD=mysecretpassword
POSTGRES_USER=admin
POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
POSTGRES_USER=$SERVICE_USER_POSTGRES
3. Replace URLs with Magic Variables
APP_URL=https://myapp.example.com
- SERVICE_URL_APP_3000
- APP_URL=$SERVICE_URL_APP
4. Remove ports: for Proxied Services
Coolify's Traefik proxy handles routing. Only keep ports: for SSH, UDP, or proxy bypass.
ports:
- "3000:3000"
environment:
- SERVICE_URL_APP_3000
5. Add Health Checks
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 5s
timeout: 10s
retries: 10
6. Use depends_on with Conditions
depends_on:
db:
condition: service_healthy
Magic Variables Reference
Coolify generates values using SERVICE_<TYPE>_<IDENTIFIER>:
| Type | Example | Result |
|---|
PASSWORD | SERVICE_PASSWORD_DB | Random password |
PASSWORD_64 | SERVICE_PASSWORD_64_KEY | 64-char password |
USER | SERVICE_USER_ADMIN | Random 16-char string |
BASE64_64 | SERVICE_BASE64_64_SECRET | 64-char random string |
REALBASE64_64 | SERVICE_REALBASE64_64_JWT | Actual base64-encoded string |
HEX_32 | SERVICE_HEX_32_KEY | 64-char hex string |
URL | SERVICE_URL_APP_3000 | https://app-uuid.example.com + proxy |
FQDN | SERVICE_FQDN_APP | app-uuid.example.com (no scheme, no port suffix) |
Declaration vs Reference (Critical)
For SERVICE_URL, the port suffix configures proxy routing but is not part of the variable name:
- SERVICE_URL_MYAPP_3000
- APP_URL=$SERVICE_URL_MYAPP
- WEBHOOK_URL=${SERVICE_URL_MYAPP}/webhooks
SERVICE_FQDN is automatically available when SERVICE_URL is declared — no separate declaration needed:
- SERVICE_URL_MYAPP_3000
- FULL_URL=$SERVICE_URL_MYAPP
- HOSTNAME=${SERVICE_FQDN_MYAPP}
⚠️ Important: Use hyphens, not underscores, before port numbers:
SERVICE_URL_MY_SERVICE_3000
SERVICE_URL_MY-SERVICE_3000
See references/magic-variables.md for complete list.
Coolify-Specific Extensions
Create Directory
volumes:
- type: bind
source: ./data
target: /app/data
is_directory: true
Create File with Content
Useful in raw mode when you can't reference external files. In repository mode, you can just mount files normally.
volumes:
- type: bind
source: ./config.json
target: /app/config.json
content: |
{"key": "${SERVICE_PASSWORD_APP}"}
volumes:
- ./config/settings.json:/app/config.json:ro
Exclude from Health Checks
For migration/init containers that exit after running:
services:
migrate:
command: ["npm", "run", "migrate"]
exclude_from_hc: true
Common Patterns
Database Connection
environment:
- DATABASE_URL=postgres://${SERVICE_USER_POSTGRES}:${SERVICE_PASSWORD_POSTGRES}@db:5432/${POSTGRES_DB:-myapp}
Shared Credentials
Same SERVICE_PASSWORD_* identifier = same value across all services:
services:
app:
environment:
- DB_PASS=$SERVICE_PASSWORD_POSTGRES
db:
environment:
- POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
Multi-Service URLs
services:
frontend:
environment:
- SERVICE_URL_FRONTEND_3000
- API_URL=$SERVICE_URL_API
api:
environment:
- SERVICE_URL_API_8080=/api
Health Check Patterns
test: ["CMD", "wget", "--spider", "-q", "http://localhost:8080"]
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
test: ["CMD", "redis-cli", "ping"]
test: ["CMD", "echo", "ok"]
Environment Variable Syntax
environment:
- NODE_ENV=production
- API_KEY=${API_KEY}
- LOG_LEVEL=${LOG_LEVEL:-info}
- SECRET=${SECRET:?}
Troubleshooting
| Problem | Solution |
|---|
| "No Available Server" error | Check docker ps for unhealthy containers; verify healthcheck passes |
| Variables not in Coolify UI | Use ${VAR} syntax; hardcoded VAR=value won't appear |
| Magic variables not generating | Check spelling; ensure SERVICE_ prefix; verify Coolify v4.0.0-beta.411+ |
| Port routing broken | Use SERVICE_URL_NAME_PORT; avoid underscores before port; remove ports: |
Examples
First, check for an official template: Many popular services have official Coolify templates at github.com/coollabsio/coolify/tree/main/templates/compose. If one exists, use it as the reference for correct patterns.
When converting a compose file without an official template, analyze it and use the matching example:
Quick analysis:
- Count the
services: — if just 1, use simple/
- Look for
postgres, mysql, mariadb, mongo images — if 1 database, use with-database/
- Look for mounted
.xml, .json, .yml config files — if present, use multi-service/
- Look for
clickhouse, redis, multiple databases — use multi-service/
References