GiGaRuN Solutions
Vos métiers, nos solutions Excellence IT - La Réunion
+262 692 31 11 91
← Retour aux guides

🚀 Coolify + Astro + Directus

Déployer un site vitrine statique avec CMS headless en auto-hébergement

1. Vue d’ensemble de la stack

Cette stack assemble quatre composants distincts pour produire un site vitrine statique performant, gérable via une interface de contenu, et déployé automatiquement à chaque modification.

Rôle de chaque composant

1

Coolify — PaaS self-hosted

Coolify joue le rôle d’orchestrateur. Il encapsule Docker, Traefik (reverse proxy), et une interface web pour gérer les applications sans toucher au terminal. Il prend en charge le build des images Docker, le déploiement des containers, la gestion des certificats SSL via Let’s Encrypt, le routing des domaines, et les webhooks GitHub pour déclencher des rebuilds automatiques sur push. C’est l’équivalent self-hosted de Railway, Render ou Heroku.

2

Astro — Static Site Generator (SSG)

Astro est un framework front-end orienté performance. En mode output: 'static', il génère uniquement du HTML, CSS et JavaScript statique dans un dossier dist/. Le point clé : Astro fetch l’API Directus au moment du build (pas au runtime). Chaque page .astro effectue ses appels HTTP vers Directus pendant la compilation. Une fois le build terminé, le site est complètement statique — aucune dépendance en production vers Directus.

3

Directus — Headless CMS

Directus est un CMS headless qui expose une API REST (et GraphQL) sur les données stockées dans PostgreSQL. L’interface d’administration permet de créer des collections (services, tarifs, témoignages, équipe...) et d’éditer les items sans toucher au code. Astro consomme cette API via fetch() au build time. Directus fournit aussi un système de Flows (automatisations internes) qui permettent de déclencher une action externe (webhook vers Coolify) quand un item est créé, modifié ou supprimé.

4

nginx — Serveur de fichiers statiques

En production, le site est servi par nginx via une image Docker Alpine légère. Le Dockerfile multistage copie le dist/ généré par Astro dans /usr/share/nginx/html. nginx répond sur le port 80. Aucun Node.js ne tourne en production — l’image finale est très légère (~25 Mo) et sécurisée.

Cycle complet de mise à jour

🔄
Durée totale : ~2 à 3 minutes entre la modification dans Directus et le site mis à jour en production.
  1. Le gestionnaire de contenu modifie ou crée un item dans Directus (ex : nouvelle offre de service)
  2. Le Flow Directus se déclenche sur l’événement items.update
  3. Le Flow effectue un POST HTTP vers l’API Coolify avec l’UUID de l’application et le paramètre &force=true
  4. Coolify lance un docker build --no-cache du projet Astro
  5. Pendant le build, Astro fetch toutes les collections Directus via l’API REST
  6. Astro génère le dist/ statique avec les nouvelles données
  7. nginx Alpine est construit avec le nouveau dist/
  8. Coolify remplace l’ancien container par le nouveau (zero-downtime si configuré)
  9. Le site est à jour
Avantage majeur du statique : Les performances sont maximales (pas de calcul côté serveur à la requête), la surface d’attaque est minimale (pas d’exécution PHP/Node en prod), et le coût de serveur est faible (nginx consème quasiment zéro ressource).

2. Limites de Directus côté usage client

Lecture obligatoire avant de proposer Directus à un client. Directus est un outil puissant mais il n’est pas adapté à tous les profils d’utilisateurs finaux.

Problèmes d’ergonomie pour les non-techniques

  • Interface pensée développeur, pas end-user : La terminologie exposée est celle d’une base de données — on parle de "Collections", "Items", "Fields", "Relations" — pas de "Pages", "Articles", "Titre de la page". Un gestionnaire de contenu non-technique sera perdu dès le premier écran.
  • Pas de WYSIWYG par défaut : Les champs texte longs sont de simples zones de saisie. Il faut configurer manuellement le type d’interface à Rich Text (WYSIWYG via TipTap) ou Markdown dans les paramètres de chaque champ. Ce n’est pas intuitif et ce n’est pas aussi riche qu’un éditeur WordPress ou Ghost.
  • Logique base de données exposée : Chaque collection correspond à une table, chaque item à une ligne. Les relations (Many-to-Many, One-to-Many) se configurent en base. Pour un utilisateur habituelé à un CMS classique, naviguer entre collections liées est déroutant.
  • Flows (webhooks internes) : L’outil d’automatisation de Directus est puissant mais la courbe d’apprentissage est élevée. L’interface de création de flows est peu intuitive (système de nodes à connecter visuellement). Un administrateur non-développeur ne peut pas le gérer seul.
  • Pas de versionning de contenu natif : Contrairement à WordPress (révisions d’articles), Directus ne conserve pas l’historique des modifications d’un item. Si un utilisateur écrase une valeur, elle est perdue. Des extensions tierces existent mais ne sont pas activées par défaut.
  • Pas adapté pour les TPE/PME sans référent technique : Si aucune personne technique n’est disponible côté client pour former et accompagner les utilisateurs, Directus génère du support inutile. Dans ce cas, préférer WordPress, Ghost, ou Strapi (meilleure UX admin).

Profil idéal pour Directus

👥
Directus convient si : le gestionnaire de contenu est à l’aise avec une interface type backoffice ERP (un comptable qui utilise Dolibarr, un gérant de PME avec un logiciel de gestion métier). Ces profils comprennent intuitivement la notion de tables/champs et de navigation par onglets sans formation longue. Directus est aussi excellent pour les développeurs qui construisent des interfaces d’administration sur-mesure en consommant l’API.
CMS UX admin WYSIWYG Versionning Profil idéal
Directus ⚠ Technique ⚠ Optionnel Non Développeurs, backoffice
WordPress ✅ Intuitif ✅ Natif ✅ Oui Tous publics
Ghost ✅ Très simple ✅ Natif Non Blog, newsletter
Strapi ⚠ Semi-technique ⚠ Plugin Non Développeurs

3. Prérequis

  • 🖥 VPS Debian 12 minimum — 2 Go RAM (4 Go recommandé pour Coolify + Directus + app + PostgreSQL). CPU : 2 vCPU minimum. Stockage : 20 Go SSD minimum.
  • 🐳 Docker installé — Coolify peut l’installer automatiquement mais avoir une version récente (24+) est préférable. docker --version && docker compose version
  • 💻 Compte GitHub avec un token d’accès personnel — scope requis : repo (lecture/écriture sur les repos privés). Le token est nécessaire pour que Coolify accède au code source et configure les webhooks automatiquement.
  • 🌐 Domaine ou entrée DNS — Un domaine pointé sur l’IP du VPS (entrée A en DNS). Pour un test local, une entrée dans /etc/hosts suffit. Coolify gère ensuite le SSL automatiquement via Let’s Encrypt si le domaine est accessible depuis internet.
  • 🔒 Port 8001 ouvert — Pour accéder à l’interface Coolify. Ports 80 et 443 ouverts pour le trafic web.
  • 📦 Node.js 18+ en local — Pour développer le projet Astro en local avant de pusher sur GitHub.
💡
Sizing recommandé pour un site vitrine PME : 4 Go RAM, 2 vCPU, 40 Go SSD. Avec ce sizing, Coolify + Directus + PostgreSQL + nginx tournent confortablement, avec de la marge pour d’autres services. Un VPS 4 Go RAM / 2 vCPU / 40 Go SSD à ~6€/mois est très adapté.

4. Installation Coolify

Script d’installation officiel

Coolify s’installe en une seule commande en root sur le VPS :

curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash

Le script installe Docker si absent, configure les réseaux Docker, télécharge les images Coolify et démarre l’interface web. L’installation complète prend 3 à 5 minutes selon la connexion.

Accès à l’interface

Coolify écoute sur le port 8000 par défaut. Accéder via : http://[IP-VPS]:8000

⚠️
Port déjà utilisé sur le VPS ?
Si le port 8000 est déjà occupé par une autre application, Coolify ne démarre pas correctement. Modifier le port dans /data/coolify/source/.env avant de lancer le démarrage :
nano /data/coolify/source/.env
# Changer APP_PORT=8000 en APP_PORT=8001 (ou tout autre port libre)

# Puis relancer :
cd /data/coolify/source && docker compose up -d
L’interface sera alors accessible sur http://[IP-VPS]:8001. Adapter toutes les références de port en conséquence (notamment l’URL dans le Flow Directus).

Créer un token API Coolify

Le token est nécessaire pour que Directus puisse appeler l’API Coolify et déclencher les rebuilds.

  1. Se connecter à l’interface Coolify
  2. Cliquer sur Profile (icône en haut à droite)
  3. API TokensNew Token
  4. Donner un nom explicite : directus-deploy-webhook
  5. Cocher TOUTES les permissions
  6. Copier et sauvegarder le token généré (il ne sera plus affiché après)

⚠ ALERTE CRITIQUE : conflit réseau Docker

🚨
Si votre VPS est sur un réseau LAN en 10.0.0.x (fréquent chez les hébergeurs cloud ou en interne), Coolify crée un pool Docker 10.0.0.0/8 qui entre directement en conflit avec le réseau physique. Résultat : le VPS devient inaccessible après l’installation, les containers n’ont plus accès à internet. Solution à appliquer AVANT ou juste après l’install.
# Créer ou modifier /etc/docker/daemon.json
nano /etc/docker/daemon.json
{
  "default-address-pools": [
    {"base": "172.20.0.0/16", "size": 24}
  ]
}
# Appliquer le changement
systemctl restart docker

# Vérifier que les containers Coolify redémarrent bien
docker ps
💬
Si le VPS est déjà inaccessible, il faut accéder via la console KVM/VNC de l’hébergeur (hors réseau Docker), appliquer la correction, et redémarrer Docker.

5. Déploiement Directus via Coolify

Création du service

  1. Dans Coolify : Services+ New Service
  2. Rechercher directus dans la liste
  3. Sélectionner directus-with-postgresql (inclut PostgreSQL dédié pour Directus)
  4. Choisir l’environnement cible (serveur + réseau)
  5. Configurer les variables d’environnement essentielles

Variables d’environnement Directus

ADMIN_EMAIL=admin@votredomaine.re
ADMIN_PASSWORD=MotDePasseForte2024!
SECRET=une-chaine-aleatoire-longue-32-caracteres-minimum

# Optionnel mais recommandé
PUBLIC_URL=https://cms.votredomaine.re
CORS_ENABLED=true
CORS_ORIGIN=https://votredomaine.re

Récupérer l’IP Docker interne de Directus

Une fois Directus déployé et démarré, récupérer son IP interne Docker. C’est cette IP qu’Astro utilisera pour fetcher l’API au moment du build.

# Lister les containers pour trouver le nom du container Directus
docker ps | grep directus

# Inspecter le container pour obtenir son IP
docker inspect [nom-container-directus] | grep IPAddress

# Exemple de résultat :
# "IPAddress": "172.20.2.4"

Configurer les collections dans Directus

  1. Accéder à l’interface Directus : http://[IP-VPS]:port-directus ou via le domaine configuré
  2. Se connecter avec les credentials ADMIN_EMAIL / ADMIN_PASSWORD
  3. Aller dans Settings → Data Model
  4. Créer les collections : services, tarifs, temoignages, equipe...
  5. Pour chaque collection, définir les champs (nom, description, ordre, actif, image...)
  6. Pour les champs texte longs : choisir l’interface Rich Text ou Markdown

Configurer les permissions publiques

Par défaut, toutes les collections sont privées. Astro fetch l’API sans token au build time (ou avec un token service statique), il faut donc autoriser la lecture publique sur les collections exposées.
  1. Settings → Access Control → Public
  2. Pour chaque collection (services, tarifs, etc.) : cocher Read
  3. Ne jamais cocher Create/Update/Delete pour le rôle Public
# Tester l'API publique depuis le VPS :
curl http://172.20.2.4:8055/items/services
# Doit retourner un JSON avec les items (pas une erreur 403)

6. Connecter Directus au réseau Coolify

Par défaut, Directus est déployé dans son propre réseau Docker isolé. L’application Astro (pendant son build) se trouve dans le réseau coolify. Ces deux réseaux ne se voient pas.

🚨
Erreur typique : fetch failed ou ECONNREFUSED pendant le build Astro — Astro ne peut pas joindre Directus car ils sont sur des réseaux Docker séparés.

Solution : connecter Directus au réseau coolify

# Connecter le container Directus au réseau coolify
docker network connect coolify [nom-container-directus]

# Vérifier que la connexion est active :
docker inspect [nom-container-directus] | grep -A5 '"coolify"'

# Vous devez voir quelque chose comme :
# "coolify": {
#     "IPAMConfig": {},
#     "Links": null,
#     "Aliases": ["directus"],
#     "IPAddress": "172.20.0.15"
# }
Cette connexion réseau est perdue après un redémarrage du container Directus (via Coolify). Pour la rendre permanente, il faut ajouter le réseau coolify dans la définition du service Directus dans Coolify (section Docker Compose avancée), ou exécuter la commande via un script de démarrage.

Utiliser l’IP interne Docker dans Astro

Dans le code Astro, utiliser l’IP Docker interne de Directus (pas localhost, pas le domaine public). L’IP interne est stable tant que le container n’est pas recréé.

// Dans le code Astro
const DIRECTUS = 'http://172.20.2.4:8055';

// Alternative plus robuste : variable d'environnement
// DIRECTUS_URL=http://172.20.2.4:8055 dans les env vars Coolify
const DIRECTUS = import.meta.env.DIRECTUS_URL;
💡
Astuce : Utiliser une variable d’environnement DIRECTUS_URL configurée dans Coolify plutot qu’une IP en dur dans le code. Si l’IP change (recreation du container Directus), il suffit de mettre à jour la variable sans toucher au code source.

7. Création du projet Astro

Initialisation

npm create astro@latest
# Choix recommandés :
# Template : Empty
# TypeScript : No (ou Strict si l’équipe maîtrise TS)
# Install dependencies : Yes
# Init git : Yes

cd mon-site-vitrine
npm run dev  # Vérifier que le projet démarre sur http://localhost:4321

Configuration astro.config.mjs

import { defineConfig } from 'astro/config';
import tailwindcss from '@tailwindcss/vite';

export default defineConfig({
  output: 'static',   // Mode SSG : génère dist/ statique
  vite: {
    plugins: [tailwindcss()],
  },
});

Installer Tailwind CSS (optionnel mais recommandé pour les sites vitrine) :

npm install -D @tailwindcss/vite tailwindcss

Fetch Directus dans une page Astro

Les pages .astro ont un bloc frontmatter (entre ---) exécuté uniquement au build time. C’est là que se font les appels API vers Directus :

---
// src/pages/index.astro

const DIRECTUS = import.meta.env.DIRECTUS_URL ?? 'http://172.20.2.4:8055';

// Fetch parallèle de plusieurs collections
const [servicesRes, tarifsRes, temoignagesRes] = await Promise.all([
  fetch(`${DIRECTUS}/items/services?sort=ordre&filter[actif][_eq]=true`),
  fetch(`${DIRECTUS}/items/tarifs?sort=ordre`),
  fetch(`${DIRECTUS}/items/temoignages?sort=-date_created&limit=6`),
]);

// Extraire les données (Directus enveloppe toujours dans { data: [...] })
const services = (await servicesRes.json()).data ?? [];
const tarifs = (await tarifsRes.json()).data ?? [];
const temoignages = (await temoignagesRes.json()).data ?? [];
---

<!-- Template HTML Astro -->
<html lang="fr">
  <head>
    <meta charset="UTF-8">
    <title>Mon Site Vitrine</title>
  </head>
  <body>
    <section id="services">
      <h2>Nos Services</h2>
      {services.map(service => (
        <article>
          <h3>{service.nom}</h3>
          <p>{service.description}</p>
        </article>
      ))}
    </section>

    <section id="tarifs">
      <h2>Nos Tarifs</h2>
      {tarifs.map(tarif => (
        <div class="tarif-card">
          <h3>{tarif.nom}</h3>
          <p class="prix">{tarif.prix} €/mois</p>
          <p>{tarif.description}</p>
        </div>
      ))}
    </section>
  </body>
</html>

Structure de projet recommandée

mon-site-vitrine/
├── src/
│   ├── pages/
│   │   ├── index.astro          # Page d’accueil
│   │   ├── services.astro       # Page services
│   │   └── contact.astro        # Page contact
│   ├── components/
│   │   ├── Header.astro
│   │   ├── Footer.astro
│   │   └── ServiceCard.astro
│   └── layouts/
│       └── Base.astro           # Layout générique
├── public/                      # Assets statiques (images, favicon)
├── astro.config.mjs
├── Dockerfile               # ◀ Créer ce fichier (voir section 8)
└── package.json

8. Dockerfile multistage (nginx)

Le Dockerfile utilise un build multistage : une première image Node.js compile le projet Astro, une deuxième image nginx légère sert uniquement le contenu statique généré.

# Stage 1 : Build Astro
FROM node:22-bookworm-slim AS builder
WORKDIR /app

# Copier les manifestes de dépendances en premier (cache Docker optimal)
COPY package*.json ./
RUN npm ci --legacy-peer-deps

# Copier le code source
COPY . .

# Build : Astro fetch Directus et génère dist/
RUN npm run build

# Stage 2 : Servir avec nginx Alpine (image finale légère)
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80

Pourquoi ce pattern ?

  • Sécurité : L’image finale ne contient pas Node.js, npm, ni le code source. Seuls les fichiers HTML/CSS/JS statiques sont présents.
  • Performance : nginx Alpine fait ~25 Mo. nginx sert du statique très efficacement, sans overhead.
  • Data fetch au build time : pendant le npm run build du stage 1, Astro appellle http://172.20.2.4:8055/items/... et intègre les données dans le HTML généré. En production, aucune requête API n’est effectuée.
  • Cache Docker : La copie des package*.json et le npm ci avant le COPY . . permettent à Docker de cacher la couche d’installation des dépendances si celles-ci n’ont pas changé. Mais attention : si Coolify utilise --no-cache (via force=true), toutes les couches sont reconstruites.

Configuration nginx personnalisée (optionnel)

Pour ajouter la gestion des routes SPA ou des en-têtes de sécurité :

# nginx.conf à placer à la racine du projet
server {
    listen 80;
    root /usr/share/nginx/html;
    index index.html;

    # Gestion 404 : renvoyer vers index.html (si routing côté client)
    location / {
        try_files $uri $uri/ /index.html;
    }

    # En-têtes de sécurité
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";
    add_header Referrer-Policy "strict-origin-when-cross-origin";

    # Cache des assets statiques
    location ~* \.(js|css|png|jpg|svg|ico|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}
# Dans le Dockerfile, remplacer le stage 2 par :
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80

9. Configuration de l’application dans Coolify

Création de l’application

  1. New ApplicationGitHub
  2. Sélectionner le repository du projet Astro
  3. Choisir la branche (généralement main)
  4. Build Pack : DockerfileIMPORTANT : ne pas choisir Nixpacks. Nixpacks détecte automatiquement Node.js et tente de lancer npm start au lieu de servir le statique via nginx.
  5. Dockerfile location : /Dockerfile (à la racine du repo)
  6. Port exposé : 80 (port nginx, pas 4321 qui est le port de dév Astro)
  7. Domain : votre domaine (ex: www.votredomaine.re)

Variables d’environnement de l’application

# Dans Coolify > Application > Environment Variables
DIRECTUS_URL=http://172.20.2.4:8055
# Ces variables sont injectées dans le container au build time
# et accessibles via import.meta.env dans Astro
🔗
Webhook GitHub : Coolify configure automatiquement un webhook sur le repo GitHub lors de la première connexion. Chaque git push sur la branche surveillée déclenchera un rebuild automatique. Inutile de le configurer manuellement.

Vérifications critiques via psql (base Coolify)

Coolify stocke la configuration des applications dans une base PostgreSQL interne. En cas de comportement anormal (mauvais port, crash au démarrage), vérifier directement en base :

# Se connecter à la base Coolify
docker exec -it coolify-db psql -U coolify

# Vérifier les paramètres clés de l'application
SELECT build_pack, ports_exposes, start_command, is_static
FROM applications
WHERE uuid='[votre-uuid-application]';

-- Résultat attendu :
-- build_pack     | ports_exposes | start_command | is_static
-- dockerfile     | 80            | (null)        | false

-- Corriger start_command s'il est renseigné (cause de crash)
UPDATE applications SET start_command = NULL WHERE uuid='[votre-uuid]';

-- S'assurer que is_static est false (true force un path buildkit incompatible)
UPDATE applications SET is_static = false WHERE uuid='[votre-uuid]';

-- Quitter psql
\q
start_command NULL est obligatoire pour que le CMD du Dockerfile s’exécute. Si Coolify insère automatiquement une commande de démarrage (ex: node dist/server/entry.mjs pour du SSR), le container crashe car cette commande n’existe pas en mode statique.

10. Flow Directus → rebuild automatique

L’automatisation centrale de la stack : quand un gestionnaire modifie du contenu dans Directus, le site se reconstruit automatiquement en ~2-3 minutes.

Création du Flow

1

Créer un nouveau Flow

Dans Directus : Flows (icône éclair dans le menu) → + (bouton création)New Flow. Donner un nom explicite : Rebuild site sur modification contenu.

2

Configurer le Trigger

Type de trigger : Event Hook. Paramètres :

  • Type : filter (synchrone) ou action (asynchrone, recommandé pour ne pas bloquer l’UI)
  • Scope : items.create, items.update, items.delete
  • Collections : sélectionner uniquement les collections qui alimentent le site (services, tarifs, temoignages...) pour éviter des rebuilds inutiles
3

Ajouter l’opération de déploiement

Sur le canvas du flow : cliquer + pour ajouter une opération.

  • Type : Request (requête HTTP)
  • Name : Deploy Coolify
  • Method : POST
  • URL : voir ci-dessous
  • Headers : Authorization: Bearer [votre-token-api-coolify]
4

Connecter le trigger à l’opération

Sur le canvas visuel, tirer une flèche du bloc Trigger vers le bloc Deploy Coolify. Sauvegarder le Flow. Le flow est maintenant actif.

URL de déploiement Coolify

POST http://[IP-coolify-interne]:8080/api/v1/deploy?uuid=[UUID-APP]&force=true

# Exemple :
POST http://172.20.0.1:8080/api/v1/deploy?uuid=abc123def456&force=true

# Header :
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGci...
🚨
Le paramètre &force=true est OBLIGATOIRE. Sans lui, Docker utilise le cache des couches et ne redémarre pas le npm run build si le code source n’a pas changé. Astro ne re-fetche alors pas Directus et le contenu reste identique à l’ancien build. Avec force=true, Coolify passe --no-cache à Docker build, forçant la reconstruction complète.

Trouver l’UUID de l’application

# Via l'interface Coolify :
# Application > Settings > l'UUID est dans l'URL et dans les infos générales

# Via l’API Coolify :
curl -H "Authorization: Bearer [token]" http://[IP]:8080/api/v1/applications
# Retourne la liste des applications avec leurs UUID

# Via la base de données Coolify :
docker exec -it coolify-db psql -U coolify -c "SELECT uuid, name FROM applications;"

Tester le Flow manuellement

# Tester l'appel API Coolify directement (depuis le VPS ou depuis Directus) :
curl -X POST \
  "http://172.20.0.1:8080/api/v1/deploy?uuid=abc123&force=true" \
  -H "Authorization: Bearer [votre-token]"

# Réponse attendue si tout est correct :
# {"message":"Deployment queued."}

11. Tableau des problèmes rencontrés et solutions

Tous les problèmes ci-dessous ont été rencontrés et résolus lors d’un déploiement réel de cette stack. Aucun n’est hypothétique.

# Symptôme Cause Solution
1 VPS inaccessible après installation Coolify. Containers sans accès internet. Coolify crée un pool Docker 10.0.0.0/8 en conflit avec le réseau LAN physique du VPS (ex: réseau hébergeur en 10.x.x.x). Modifier /etc/docker/daemon.json pour utiliser 172.20.0.0/16 comme pool d’adresses. Accéder via console KVM si le SSH est coupé. Redem’arrer Docker.
2 Bad Gateway 502 après chaque rebuild réussi. Coolify configure les labels Traefik avec le port 4321 (port de prévisualisation Astro) au lieu du port 80 (nginx). Traefik tente de joindre le port 4321 qui n’existe pas dans le container nginx. Vider start_command (mettre NULL en DB), s’assurer que is_static=false, vérifier que ports_exposes=80 en base Coolify. Reconstruire l’application.
3 Build échoué : network mode coolify not supported by buildkit Coolify utilise --network [réseau docker] dans le path is_static=true, ce qui n’est pas supporté par buildkit. Le path is_static=false utilise --network host qui fonctionne. Mettre is_static=false dans application_settings en base de données Coolify. La différence entre les deux paths est dans le code source de Coolify.
4 Rebuild terminé avec succès mais le contenu Directus n’est pas mis à jour sur le site. Docker met en cache les couches du build. Si le code source n’a pas changé, la couche npm run build est réutilisée depuis le cache et Astro ne re-fetche pas Directus. Appeler l’API Coolify avec &force=true dans le Flow Directus. Ce paramètre passe --no-cache à docker build, forçant la reconstruction intégrale.
5 Blocked request. This host is not allowed. en utilisant astro preview en prod. Vite 8+ (utilisé par Astro) a une protection stricte sur les hosts non autorisés. astro preview refuse les requêtes provenant de Traefik avec un hostname différent de localhost. Ne jamais utiliser astro preview en production. Utiliser le Dockerfile multistage avec nginx Alpine. CMD du Dockerfile = nginx, pas astro preview.
6 Container crash au démarrage : Cannot find module dist/server/entry.mjs Coolify a configuré start_command pour exécuter un point d’entrée SSR (node dist/server/entry.mjs) alors que le projet est en mode output: 'static' et ne génère pas ce fichier. Mettre start_command = NULL en base de données Coolify pour laisser le CMD du Dockerfile s’exécuter (nginx). Reconstruire l’application.

12. Résumé des commandes utiles

Inspecter les containers Docker

# Lister tous les containers actifs
docker ps

# Lister tous les containers (y compris arrêtés)
docker ps -a

# Voir les réseaux Docker d’un container
docker inspect [nom-container] | grep -A20 '"Networks"'

# Voir l’IP d’un container
docker inspect [nom-container] | grep IPAddress

# Voir les variables d’environnement d’un container
docker inspect [nom-container] | grep -A50 '"Env"'

Logs de déploiement Coolify

# Logs temps réel du daemon Coolify
docker logs -f coolify

# Logs du container applicatif (site Astro/nginx)
docker logs -f [nom-container-app]

# Logs du container Directus
docker logs -f [nom-container-directus]

# Logs en temps réel avec filtre
docker logs -f [container] 2>&1 | grep -i "error\|warn\|build"

Accès à la base de données Coolify

# Entrer dans psql sur la base Coolify
docker exec -it coolify-db psql -U coolify

# Requetes utiles une fois connecté :
\dt                          -- lister toutes les tables
\d applications              -- structure de la table applications

-- Voir toutes les applications
SELECT uuid, name, build_pack, ports_exposes, start_command, is_static
FROM applications;

-- Corriger le port exposé
UPDATE applications SET ports_exposes = '80' WHERE uuid='[votre-uuid]';

-- Vider la commande de démarrage
UPDATE applications SET start_command = NULL WHERE uuid='[votre-uuid]';

-- Désactiver le mode static
UPDATE applications SET is_static = false WHERE uuid='[votre-uuid]';

-- Quitter
\q

Tester l’API Coolify

# Récupérer les informations du serveur
curl -H "Authorization: Bearer [token]" http://localhost:8080/api/v1/servers

# Lister les applications
curl -H "Authorization: Bearer [token]" http://localhost:8080/api/v1/applications

# Déclencher un déploiement manuellement
curl -X POST \
  -H "Authorization: Bearer [token]" \
  "http://localhost:8080/api/v1/deploy?uuid=[APP-UUID]&force=true"

# Vérifier les déploiements en cours
curl -H "Authorization: Bearer [token]" \
  "http://localhost:8080/api/v1/applications/[APP-UUID]/deployments"

Tester Directus depuis un autre container

# Lancer un container temporaire sur le réseau coolify pour tester l'accès Directus
docker run --rm --network coolify curlimages/curl \
  http://[IP-DIRECTUS]:8055/items/services

# Tester depuis le host (si Directus expose un port sur le host)
curl http://localhost:8055/items/services

# Tester avec authentification (si les permissions publiques ne sont pas activées)
curl -H "Authorization: Bearer [token-admin-directus]" \
  http://[IP-DIRECTUS]:8055/items/services

Gestion des réseaux Docker

# Lister tous les réseaux Docker
docker network ls

# Inspecter le réseau coolify
docker network inspect coolify

# Connecter un container à un réseau
docker network connect coolify [nom-container]

# Déconnecter un container d’un réseau
docker network disconnect coolify [nom-container]

Commandes build locales (test avant push)

# Builder l'image Docker localement
docker build -t mon-site-vitrine:test .

# Lancer le container pour tester
docker run -p 8080:80 mon-site-vitrine:test

# Accéder sur http://localhost:8080

# Nettoyer les images de test
docker rmi mon-site-vitrine:test

# Voir la taille de l'image finale
docker images mon-site-vitrine:test
Checklist de validation finale :
  • Directus accessible et collections visibles via l’API publique
  • Container Directus connecté au réseau coolify
  • Variable DIRECTUS_URL configurée dans Coolify avec l’IP interne Docker
  • Build Pack = dockerfile, Port exposé = 80, start_command = NULL
  • Flow Directus créé avec force=true dans l’URL Coolify
  • Test end-to-end : modifier un item dans Directus → attendre 2-3 min → vérifier le site