Files
courcellematthi/DEPLOIEMENT-COOLIFY.md
T
2026-08-05 22:40:03 +02:00

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 :

  1. index.html — la balise canonical, les og:url, les hreflang, et les URL absolues du JSON-LD
  2. sitemap.xml — la balise <loc>
  3. robots.txt — la ligne Sitemap:

É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-Security avec max-age=63072000 indique 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

  1. Google Search Console — ajoute la propriété et soumets https://www.courcellematthi.eu/sitemap.xml
  2. Test des résultats enrichis — vérifie le JSON-LD sur search.google.com/test/rich-results
  3. 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)
  4. securityheaders.com — la note attendue est A ou A+
  5. 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