| name | coolify |
| description | Complete Coolify deployment and management: CLI operations, API access, Docker patterns, networking, Prometheus/Grafana, WordPress troubleshooting, SSL. Use for deploy, debug, manage Coolify services. |
Coolify — Deployment & Management
Complete guide for deploying and managing applications on Coolify.
Part 1: CLI Setup & Operations
Installation
curl -fsSL https://get.coolify.io/cli | bash
mkdir -p ~/.local/bin
curl -L https://github.com/coollabsio/coolify-cli/releases/latest/download/coolify-linux-amd64 -o ~/.local/bin/coolify
chmod +x ~/.local/bin/coolify
export PATH="$HOME/.local/bin:$PATH"
Configure Context
coolify context add production https://coolify.example.com YOUR_API_TOKEN
coolify context list
coolify context use staging
coolify context verify
Get API token from Coolify dashboard: /security/api-tokens
Service Management
coolify resource list
coolify service get SERVICE_UUID
coolify service start SERVICE_UUID
coolify service stop SERVICE_UUID
coolify service restart SERVICE_UUID
coolify app get APP_UUID
coolify app logs APP_UUID --lines 500
coolify app start APP_UUID
coolify app restart APP_UUID
coolify database list
coolify database start DB_UUID
coolify database backup DB_UUID
Deployments
coolify deploy APP_UUID
coolify deploy list APP_UUID
coolify deploy get DEPLOY_UUID
Environment Variables
coolify app env list APP_UUID
coolify app env set APP_UUID DATABASE_URL "postgresql://..."
coolify app env delete APP_UUID OLD_VAR
coolify app restart APP_UUID
Server Management
coolify server list -s
coolify server validate SERVER_UUID
coolify server domains SERVER_UUID
JSON Output for Scripting
coolify resource list --format json | jq '.[] | select(.status | contains("unhealthy"))'
coolify service list --format json | jq -r '.[].uuid'
coolify resource list --format json | jq '.[] | select(.type=="application" and .status=="running")'
Part 2: API Direct Access
When CLI doesn't support an operation:
curl -H "Authorization: Bearer $API_TOKEN" \
https://coolify.example.com/api/v1/services/SERVICE_UUID
curl -H "Authorization: Bearer $API_TOKEN" \
https://coolify.example.com/api/v1/applications
curl -X POST -H "Authorization: Bearer $API_TOKEN" \
https://coolify.example.com/api/v1/applications/APP_UUID/deploy
Part 3: Docker Patterns
Dynamic Container Names Problem
Coolify appends unique suffixes on every deploy:
my-app-eg488k8w0o44o80800wwws4c-214629924110 # Deploy 1
my-app-eg488k8w0o44o80800wwws4c-222239183078 # Deploy 2
Solution: Use DNS Aliases
Coolify creates stable DNS aliases:
{
"DNSNames": [
"my-app-eg488k8w0o44o80800wwws4c-222708068545",
"my-app",
"3e280ed24989"
]
}
docker inspect $(docker ps -q -f name=my-app) \
--format '{{json .NetworkSettings.Networks}}' | \
jq -r '.[].DNSNames[] | select(. | test("^[a-z]+-[a-z]+$"))'
Network Configuration
docker inspect CONTAINER --format '{{json .NetworkSettings.Networks}}' | jq 'keys[]'
docker inspect CONTAINER --format '{{json .NetworkSettings.Networks}}' | jq 'has("coolify")'
docker network connect coolify CONTAINER
Cross-stack communication: Enable "Connect to Predefined Network" in Coolify UI.
Environment Variable Gotchas
Coolify UI can save placeholder text as values:
Solution: Detect placeholders in code
def get_env(key: str, default: str = None) -> str:
val = os.getenv(key, default)
if val and val.lower() in ("required", "todo", key.lower()):
return default
return val
Docker Compose Best Practices
services:
api:
container_name: my-api
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:8000/health']
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
networks:
default:
external: true
name: coolify
Part 4: Prometheus + Grafana
Architecture
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Your App │────▶│ Prometheus │────▶│ Grafana │
│ :8001 │ │ :9090 │ │ :3000 │
└─────────────┘ └─────────────┘ └─────────────┘
coolify network (shared)
Prometheus Config
global:
scrape_interval: 15s
scrape_configs:
- job_name: my-app
static_configs:
- targets: ['my-app:8001']
metrics_path: /metrics
Grafana Datasource
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
url: http://prometheus:9090
isDefault: true
Port Note
Avoid port 8000 — reserved by Coolify. Use 8001+ for metrics.
Part 5: WordPress Troubleshooting
Access Container
Via Coolify dashboard → Service → Terminal → select "wordpress"
Or via SSH:
docker exec -it CONTAINER bash
cd /var/www/html
Site Down After .htaccess Edit
cat /var/www/html/.htaccess
sed -i '$d' /var/www/html/.htaccess
PHP Configuration
echo "php_value max_input_vars 3000" >> /var/www/html/.htaccess
echo "php_value upload_max_filesize 64M" >> /var/www/html/.htaccess
echo "php_value post_max_size 128M" >> /var/www/html/.htaccess
REST API Issues
Test externally first:
curl https://site.com/wp-json/
If JSON returns, it's a false positive — Site Health loopback is blocked, not the API.
Ensure .htaccess has:
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
Part 6: SSL Certificates
Coolify uses Traefik with Let's Encrypt (auto-renewal).
Check Certificate
echo | openssl s_client -servername domain.com -connect domain.com:443 2>/dev/null | \
openssl x509 -noout -dates -subject
Fix Invalid Certificate
- Coolify dashboard → Service → Domains
- Click regenerate SSL
- Verify Traefik labels in service config
Part 7: Shared Environment Variables (Team Variables)
Coolify supports team-level shared variables referenced via {{ team.VARIABLE_NAME }} in app env vars. These are encrypted in the shared_environment_variables table.
⚠️ API Limitation (v4.0.0-beta.468)
The REST API does NOT expose shared variable CRUD endpoints. The {{ team.X }} syntax works when setting app env vars, but you cannot list/create/delete shared variables via API.
Creating Shared Variables (artisan tinker)
The only reliable programmatic method:
sshpass -p 'PASSWORD' ssh user@COOLIFY_HOST \
"docker exec coolify php artisan tinker --execute=\"
\\\$var = new \App\Models\SharedEnvironmentVariable;
\\\$var->key = 'mybot';
\\\$var->value = 'TOKEN_VALUE';
\\\$var->team_id = 0;
\\\$var->save();
echo 'Created: ' . \\\$var->id;
\""
Listing Shared Variables
sshpass -p 'PASSWORD' ssh user@COOLIFY_HOST \
"docker exec \$(docker ps -q -f name=coolify-db) psql -U coolify \
-c 'SELECT id, key, team_id FROM shared_environment_variables ORDER BY key;'"
Values are Laravel-encrypted (base64 blobs) — cannot read plaintext from DB.
Creating App Env Vars (artisan tinker)
The REST API POST /applications/{uuid}/envs creates duplicate entries (bug in beta). Use artisan tinker instead:
sshpass -p 'PASSWORD' ssh user@COOLIFY_HOST << 'EOF'
docker exec coolify php artisan tinker --execute="
\$v = \App\Models\EnvironmentVariable::create([
'key' => 'DATABASE_URL',
'value' => '{{ team.PROD_DB_URL }}',
'resourceable_type' => 'App\\\Models\\\Application',
'resourceable_id' => APP_ID, // from applications table
'is_preview' => false,
'is_runtime' => true,
'is_buildtime' => true,
'is_shared' => true, // true when using {{ team.X }}
]);
echo 'Created id=' . \$v->id;
"
EOF
Important: Coolify's model observer auto-creates a preview entry. Creating one runtime entry produces TWO rows (runtime + preview) — this is correct. Don't manually create preview entries.
Gotchas
| Issue | Detail |
|---|
| API POST duplicates | REST API creates 2 entries per POST (build + runtime). Use artisan tinker instead. |
| f-string brace eating | Python f"{{ team.{name} }}" produces SINGLE braces. Use raw strings or quadruple braces f"{{{{ team.{name} }}}}". |
| Preview entries are normal | Each key has 2 DB rows (runtime: is_preview=f, is_runtime=t, is_buildtime=t + preview: is_preview=t, is_runtime=f, is_buildtime=f). The API shows both — this looks like duplicates but is correct. |
| Values encrypted | value column uses Laravel encryption. Cannot read/compare directly in DB. |
| Delete auto-created extras | If you accidentally create 3 rows, delete the middle one: is_preview=t AND is_runtime=t AND is_buildtime=t. |
Standard Bot Env Template
Every domcom bot app should have these 8 vars:
DATABASE_URL = {{ team.PROD_DB_URL }} 🔗 shared
TELEGRAM_BOT_TOKEN_XX = {{ team.xxbot }} 🔗 shared
COLLAGE_BOT_TOKEN = {{ team.alertmalertbot }} 🔗 shared
REDIS_URL = redis://parser-redis:6379/0
LOG_LEVEL = INFO
ADMIN_PASSWORD = domcom2024
ADMIN_SECRET_KEY = xx-admin-secret-key-change-in-prod
PAYMENT_STUB_MODE = false
Finding App IDs
sshpass -p 'PASSWORD' ssh user@COOLIFY_HOST \
"docker exec \$(docker ps -q -f name=coolify-db) psql -U coolify \
-c \"SELECT id, uuid, name FROM applications WHERE name LIKE '%bot%' ORDER BY name;\""
Part 8: Debugging
Quick Commands
docker ps | grep name
docker logs CONTAINER --tail 100
docker exec CONTAINER printenv | grep KEY
docker exec CONTAINER curl -s http://other-service:port/health
docker exec CONTAINER nslookup other-service
docker inspect CONTAINER | jq '.[0].State.Health'
Prometheus Target Health
curl -s 'http://localhost:9090/api/v1/targets' | \
jq '.data.activeTargets[] | {job: .labels.job, health: .health, lastError: .lastError}'
Common Issues
| Issue | Cause | Fix |
|---|
no such host | Wrong network | Enable "Connect to Predefined Network" |
Target down | Dynamic container name | Use stable DNS alias |
| Env var placeholder | UI saved placeholder | Detect in code or fix in UI |
| 502 Bad Gateway | Container unhealthy | Check health checks |
| Port conflict | Port 8000 used | Use 8001+ |
Quick Reference
Troubleshooting Workflow
Service Down?
├── Check status: coolify resource list
├── Get details: coolify service get UUID
├── Check logs: coolify app logs UUID
├── Identify issue
├── Fix (restart, config, files)
└── Verify: coolify resource list
CLI Cheatsheet
coolify context add NAME URL TOKEN
coolify context use NAME
coolify resource list
coolify service restart UUID
coolify app logs UUID
coolify deploy UUID
coolify app env set UUID KEY "value"
coolify app restart UUID
Docker Cheatsheet
docker inspect CONTAINER --format '{{json .NetworkSettings.Networks}}' | jq '.[].DNSNames'
docker inspect CONTAINER | jq '.[0].NetworkSettings.Networks | keys'
docker network connect coolify CONTAINER