11 KiB
Déploiement sur Coolify
Guide complet pour mettre courcellematthi.eu en ligne depuis Coolify.
Résumé
| Type d'application | Dockerfile (recommandé) ou Docker Compose |
| Port interne | 80 |
| Image finale | ~50 Mo |
| Build | Aucune compilation — copie de fichiers statiques |
| TLS | Géré par Traefik, pas par le conteneur |
Étape 1 — Préparer le dépôt Git
Coolify déploie depuis un dépôt. Sur ton Gitea auto-hébergé :
cd courcellematthi
git init
git add .
git commit -m "Site personnel — version initiale"
git branch -M main
git remote add origin https://git.ghostinthemachine.fr/matthieu/courcellematthi.git
git push -u origin main
Vérifie que .htaccess et .well-known/ sont bien poussés — Git les suit
normalement, mais un .gitignore trop large les fait parfois disparaître :
git ls-files | grep -E "htaccess|well-known"
Étape 2 — Configurer le DNS
Avant de créer l'application, les enregistrements doivent pointer vers le serveur, sinon Let's Encrypt échouera à émettre le certificat.
| Type | Nom | Valeur |
|---|---|---|
| A | www |
IP de ton serveur |
| A | @ |
IP de ton serveur |
Si le domaine est chez Cloudflare, désactive le proxy (nuage orange → gris) pendant l'émission du certificat. Le proxy Cloudflare intercepte le challenge HTTP-01 et fait échouer la validation ACME. Tu pourras le réactiver ensuite.
Vérification :
dig +short www.courcellematthi.eu
dig +short courcellematthi.eu
Étape 3 — Créer l'application dans Coolify
Interface Coolify → Projet → + New → Application → Public/Private Repository
Renseigne :
- Repository : l'URL de ton dépôt Gitea
- Branch :
main - Build Pack :
Dockerfile - Dockerfile Location :
/Dockerfile - Base Directory :
/ - Port Exposes :
80
Ne choisis pas Nixpacks. Il tenterait de détecter un projet Node, échouerait faute de
package.json, ou pire, installerait une chaîne de build inutile. Le Dockerfile fourni est explicite et reproductible.
Étape 4 — Domaines
Dans Configuration → General → Domains, saisis :
https://www.courcellematthi.eu
Coolify génère alors automatiquement les étiquettes Traefik et demande le certificat Let's Encrypt.
Redirection du domaine nu
Ajoute https://courcellematthi.eu comme second domaine, puis active
Redirect to primary domain (ou l'option www ↔ non-www selon ta version
de Coolify).
Ce point n'est pas cosmétique : le HTML déclare
<link rel="canonical" href="https://www.courcellematthi.eu/">. Si le domaine
nu répond aussi en 200 au lieu de rediriger, les moteurs voient deux URL pour
le même contenu et diluent le référencement.
Si tu préfères le domaine nu, il faut modifier trois endroits pour rester cohérent :
index.html— la balisecanonical, lesog:url, leshreflang, et les URL absolues du JSON-LDsitemap.xml— la balise<loc>robots.txt— la ligneSitemap:
Étape 5 — Déployer
Bouton Deploy. Le build prend une trentaine de secondes : il n'y a rien à compiler, seulement à copier.
Suis les journaux dans l'onglet Logs. Une fin de build normale ressemble à :
naming to docker.io/library/xxxxx done
Container started
Étape 6 — Vérifications post-déploiement
# Le site répond
curl -I https://www.courcellematthi.eu
# La sonde de santé
curl https://www.courcellematthi.eu/healthz # -> ok
# Le domaine nu redirige (301 attendu, pas 200)
curl -I https://courcellematthi.eu
# Les en-têtes de sécurité sont présents
curl -sI https://www.courcellematthi.eu | grep -iE "content-security|x-content-type|referrer|permissions"
# La compression fonctionne
curl -sI -H "Accept-Encoding: gzip" https://www.courcellematthi.eu | grep -i encoding
# Les fichiers de service sont servis
curl -s https://www.courcellematthi.eu/robots.txt | head -3
curl -s https://www.courcellematthi.eu/llms.txt | head -3
curl -sI https://www.courcellematthi.eu/.well-known/security.txt
Les deux pannes classiques sur Coolify
Ce sont les deux mêmes que tu as déjà rencontrées sur d'autres déploiements.
1. Conteneur absent du réseau coolify
Symptôme : Traefik renvoie 404, alors que le conteneur tourne et que ses journaux sont vides. La requête ne l'atteint jamais.
Diagnostic :
docker inspect <nom_du_conteneur> --format '{{json .NetworkSettings.Networks}}' | python3 -m json.tool
Si coolify n'apparaît pas dans la liste :
docker network connect coolify <nom_du_conteneur>
En mode Docker Compose, le fichier fourni déclare déjà le réseau. En mode Dockerfile, Coolify le fait normalement seul — mais pas toujours après une mise à jour de Coolify.
2. Port du load balancer non déclaré
Symptôme : 502 Bad Gateway, ou service not found dans le tableau de
bord Traefik.
Traefik ne devine le port que si le conteneur n'en expose qu'un seul, et cette détection échoue régulièrement. Il faut donc le déclarer explicitement.
En mode Docker Compose, l'étiquette est déjà présente :
- traefik.http.services.courcellematthi.loadbalancer.server.port=80
En mode Dockerfile, va dans Configuration → Advanced → Custom Labels et ajoute :
traefik.http.services.<nom-généré-par-coolify>.loadbalancer.server.port=80
Récupère le nom généré dans les étiquettes existantes du conteneur :
docker inspect <conteneur> --format '{{json .Config.Labels}}' | grep -o 'traefik[^"]*'
HSTS — à traiter en dernier, et prudemment
Le HSTS n'est pas émis par le conteneur. C'est un choix délibéré : le conteneur est redéployé souvent, et un réglage irréversible n'a rien à faire dans une couche jetable.
C'est Traefik qui doit l'émettre, une fois le HTTPS confirmé fonctionnel.
Pourquoi cette prudence.
Strict-Transport-Securityavecmax-age=63072000indique au navigateur de refuser toute connexion HTTP à ce domaine pendant deux ans. Si le certificat casse ensuite, le site devient inaccessible — et tu ne peux rien faire côté serveur pour l'annuler : la directive est mémorisée par le navigateur du visiteur.
Procédure recommandée
Phase 1 — validation. Configure max-age=300 (cinq minutes). En mode
Docker Compose, c'est déjà la valeur du fichier fourni. En mode Dockerfile,
ajoute dans Custom Labels :
traefik.http.middlewares.hsts-test.headers.stsSeconds=300
traefik.http.middlewares.hsts-test.headers.forceSTSHeader=true
traefik.http.routers.<nom-du-routeur>.middlewares=hsts-test
Vérifie sur plusieurs jours que le certificat se renouvelle bien.
Phase 2 — activation. Passe stsSeconds à 63072000.
N'inscris le domaine sur la liste de préchargement HSTS (hstspreload.org) que si tu es certain de ne jamais revenir en arrière : la radiation prend des mois.
Redéploiement
Automatique
Dans Configuration → General, active Auto Deploy. Coolify crée un
webhook dans Gitea : chaque git push sur main déclenche un déploiement.
C'est pertinent ici — un site statique sans test à faire tourner ne risque rien à se redéployer automatiquement.
Manuel
Bouton Redeploy.
Après une modification de contenu
Pense à mettre à jour la date dans sitemap.xml :
<lastmod>2026-08-05</lastmod>
Test local avant déploiement
Si tu veux vérifier l'image sur ta machine :
docker build -t courcellematthi .
docker run --rm -p 8080:80 courcellematthi
# puis http://localhost:8080
Contrôles utiles :
# Poids de l'image
docker images courcellematthi
# Le dossier deploy/ ne doit PAS être dans l'image
docker run --rm courcellematthi ls /usr/share/nginx/html
# La configuration Nginx est syntaxiquement correcte
docker run --rm courcellematthi nginx -t
Une fois en ligne
- Google Search Console — ajoute la propriété et soumets
https://www.courcellematthi.eu/sitemap.xml - Test des résultats enrichis — vérifie le JSON-LD sur
search.google.com/test/rich-results - Débogueur LinkedIn — force le rafraîchissement de l'aperçu de partage
sur
linkedin.com/post-inspector(LinkedIn met les aperçus en cache longtemps : sans cette étape, un ancien aperçu peut persister des semaines) - securityheaders.com — la note attendue est A ou A+
- PageSpeed Insights — contrôle les Core Web Vitals
Si quelque chose ne va pas
| Symptôme | Cause probable |
|---|---|
failed to read dockerfile: open Dockerfile: no such file or directory avec transferring dockerfile: 2B |
Dockerfile listé dans .dockerignore — voir ci-dessous |
failed to read dockerfile sans la ligne 2B |
Fichier réellement absent du dépôt, ou mauvais Dockerfile Location dans Coolify |
| 404 Traefik, conteneur sain | Conteneur hors du réseau coolify |
| 502 Bad Gateway | Étiquette loadbalancer.server.port absente |
| Certificat non émis | DNS non propagé, ou proxy Cloudflare actif |
| Page blanche, CSS absent | Chemins /assets/... — vérifier que la racine servie est bien /usr/share/nginx/html |
| Polices non chargées | CSP trop stricte — fonts.googleapis.com et fonts.gstatic.com doivent être autorisés |
| Ancienne version affichée | Cache navigateur sur index.html — vérifier l'en-tête Cache-Control: must-revalidate |
| Boucle de redirection | Redirection HTTPS dupliquée : Traefik et Nginx. Le deploy/nginx.conf fourni n'en contient pas — vérifier qu'on n'a pas utilisé nginx.conf.example par erreur |
Le Dockerfile filtré par .dockerignore
Journal caractéristique :
#1 transferring dockerfile: 2B done
ERROR: failed to solve: failed to read dockerfile:
open Dockerfile: no such file or directory
La ligne 2B est le signal décisif : BuildKit a bien reçu un contexte, mais
le Dockerfile qu'il en extrait est vide. Le fichier existe donc dans le dépôt —
c'est .dockerignore qui l'a rendu invisible.
BuildKit, utilisé par Coolify et par Docker depuis la version 23, applique les exclusions avant de lire le Dockerfile. Le builder classique tolérait ce cas ; BuildKit non.
Vérification :
grep -nE '^\s*(Dockerfile|\.dockerignore)\s*$' .dockerignore
Si l'une des deux lignes ressort, supprime-la et repousse. Ces fichiers n'ont
pas besoin d'y figurer : le RUN rm du Dockerfile les retire déjà de l'image
finale, donc ils ne sont jamais servis au public.
Vérifier que le fichier est bien dans le dépôt
Si la ligne 2B n'apparaît pas, le fichier est réellement absent :
git ls-files | grep -E '^Dockerfile$'
Sans résultat, il n'a pas été commité :
git add Dockerfile .dockerignore deploy/
git commit -m "Ajout des fichiers de déploiement"
git push
Contrôle également, dans Configuration → Build de Coolify :
- Base Directory :
/ - Dockerfile Location :
/Dockerfile
Si le dépôt contient le site dans un sous-dossier, ces deux chemins doivent être ajustés en conséquence.
Journaux :
docker logs -f <nom_du_conteneur> # Conteneur du site
docker logs -f coolify-proxy # Traefik