Mythe répandu (et écrit en commentaire dans plein de templates) : « Sur Coolify, le ports: du compose est ignoré, Coolify utilise son proxy Traefik à la place. » Faux. Coolify v4 lance docker compose up -d tel quel, le ports: est respecté à la lettre, et ça peut conflicter avec d’autres services.
Symptôme
Deploy d’une app Compose simple :
services:
app:
expose:
- "3000"
ports:
- "3000:3000" # "ignoré par Coolify" (sic)
environment:
SERVICE_FQDN_APP_3000: ${SERVICE_FQDN_APP_3000:-}
Échec :
Error response from daemon: failed to set up container networking:
driver failed programming external connectivity on endpoint app-XXX:
Bind for 10.8.0.1:3000 failed: port is already allocated
Et 10.8.0.1:3000 est utilisé par mon Gitea. Donc deux services qui s’écrasent.
Pourquoi 10.8.0.1:3000 et pas 0.0.0.0:3000 ?
Le daemon Docker de srv-a a "ip": "10.8.0.1" configuré dans /etc/docker/daemon.json (sécurité : ne pas exposer les containers sur l’IP publique par défaut, seulement sur l’IP mesh WG). Donc tout ports: 3000:3000 bind par défaut sur 10.8.0.1:3000, pas 0.0.0.0:3000. Conflit avec Gitea sur la même IP/port.
Pourquoi Coolify ne strippe pas ports: ?
Architecture Coolify v4 : Traefik est configuré pour router en interne via le network Docker (label traefik.http.routers.<service>.rule=Host(\). Il n’a PAS besoin du port host. Donc en théorie expose:seul suffit,ports:` est inutile pour Coolify.
Mais Coolify ne fait pas de modification au compose : il génère un .env avec les FQDN, lance docker compose up, et c’est tout. Si ton compose a un ports:, Docker le respecte → bind host.
Donc le mythe vient peut-être du fait que ports: est superflu dans une app Coolify-seule, pas que Coolify l’ignore.
Solution propre
Pour une app destinée à un seul environnement (Coolify), retirer le ports: du compose. Garder uniquement expose:. Traefik routera via le network interne.
Pour une app multi-env (Coolify + local dev avec accès direct au port host) :
- Option A : 2 compose files.
docker-compose.ymlsansports:,docker-compose.local.ymlqui surcharge avecports:. Etdocker compose -f docker-compose.yml -f docker-compose.local.yml upen local. - Option B :
ports:avec bind explicite sur loopback :ports: ["127.0.0.1:3000:3000"]. Inutile sur Coolify (qui passe par Traefik) mais ne conflicte plus avec une IP routable.
Anti-mémo
Quand tu vois ports: dans un compose Coolify-ready : c’est presque toujours soit du copier-coller d’un README local, soit un commentaire optimiste. Le supprimer avant le premier deploy économise un debug.