Développement Web

Déployer Directus sur Coolify avec Postgres : tutoriel complet 2026

16 min de lecture

📍 Article principal de la série : Directus 2026 : guide pratique.

Trente minutes pour transformer un VPS vide en serveur Directus opérationnel. méthode pratique chez plusieurs PME francophones d’Afrique de l’Ouest, avec coût total 4,51 € par mois.

Prérequis

  • Hetzner CX23 minimum (Postgres + Directus + MinIO confortables).
  • Coolify v4 installé.
  • Nom de domaine DNS A : cms.votre-entreprise.com.
  • Niveau attendu : intermédiaire.
  • Temps estimé : 30-45 minutes.

Pour déployer Directus headless CMS sur Coolify, vous avez besoin d’un VPS Hetzner CX22 (4 Go RAM minimum, recommandé pour 50+ utilisateurs simultanés). Coolify v4 déjà installé. Postgres 15+ recommandé (Coolify provisionne). Un domaine pour cms.example.sn. Comptez 30-45 minutes pour cette installation initiale, puis 1-2 heures de modélisation des collections avant ouverture aux éditeurs de contenu.

Étape 1 — DNS

dig +short cms.votre-entreprise.com  # IP VPS

Configurez le DNS A pour cms.example.sn pointant vers l’IP du VPS Hetzner. Vérifiez la propagation avec dig. Pour les performances ouest-africaines, mettez Cloudflare devant via un CNAME (cdn-cms.example.sn) qui pointe vers cms.example.sn. Le CDN cache les assets statiques (images uploadées) et réduit la latence pour les utilisateurs distants à Cotonou ou Bamako.

Étape 2 — Service Directus dans Coolify

Resources → + New → Service → rechercher « Directus ». Coolify déploie le template officiel avec Postgres 16 inclus.

Dans Coolify, Add new resource puis Service puis Directus. Le template officiel inclut Directus, Postgres et Redis (cache). Coolify précompose le compose YAML avec les volumes persistants pour /directus/uploads et /directus/extensions. Assignez à un projet (Content Management ou CMS). Le compose utilise directus/directus:11 (LTS au moment de cet article).

Étape 3 — Variables d’environnement

KEY=générer-uuid-v4
SECRET=générer-32-octets-base64
ADMIN_EMAIL=admin@votre-entreprise.com
ADMIN_PASSWORD=mot-de-passe-très-fort

DB_CLIENT=pg
DB_HOST=directus-db
DB_PORT=5432
DB_DATABASE=directus
DB_USER=directus
DB_PASSWORD=mot-de-passe-fort

PUBLIC_URL=https://cms.votre-entreprise.com
CACHE_ENABLED=true
CACHE_STORE=redis
REDIS=redis://directus-redis:6379

# MinIO storage
STORAGE_LOCATIONS=local,s3
STORAGE_LOCAL_DRIVER=local
STORAGE_LOCAL_ROOT=/uploads
STORAGE_S3_DRIVER=s3
STORAGE_S3_KEY=...
STORAGE_S3_SECRET=...
STORAGE_S3_BUCKET=directus
STORAGE_S3_REGION=eu-central-1
STORAGE_S3_ENDPOINT=https://minio.votre-entreprise.com

Variables critiques Directus. PUBLIC_URL avec l’URL HTTPS complète. KEY et SECRET générés via openssl rand -base64 32 (deux secrets différents). DB_CLIENT=pg, DB_HOST/DB_PORT/DB_DATABASE/DB_USER/DB_PASSWORD pré-remplis par Coolify. CACHE_ENABLED=true et CACHE_STORE=redis (avec REDIS=redis://…). EMAIL_FROM, EMAIL_TRANSPORT=smtp avec les credentials SMTP. ADMIN_EMAIL et ADMIN_PASSWORD pour le compte super-admin créé au premier démarrage.

Étape 4 — Domaine HTTPS

Onglet Domains : https://cms.votre-entreprise.com. Force HTTPS.

Dans Coolify, onglet Domains du service Directus. Ajoutez cms.example.sn et cochez Force HTTPS Redirect. Coolify utilise Traefik pour générer le certificat Let’s Encrypt automatiquement. Activez aussi WebSocket dans la config Traefik (essentiel pour le temps réel Directus, notamment Collaborative Editing en bêta). Vérifiez après 1-2 minutes que https://cms.example.sn répond.

Étape 5 — Lancer Deploy

Coolify pull directus/directus + postgres + redis. Comptez 3 minutes.

Cliquez Deploy. Directus démarre en 2-5 minutes : Postgres s’initialise avec le schéma système Directus, Redis démarre, l’app Directus se lance sur le port 8055 interne. Suivez avec Logs Coolify. Erreur typique : KEY ou SECRET trop courte (Directus refuse les secrets sous 32 caractères). Le service est prêt quand vous voyez ‘Server started successfully’.

Étape 6 — Premier login Studio

Ouvrir https://cms.votre-entreprise.com/admin. Saisir email + password admin définis. Studio s’ouvre.

Ouvrez https://cms.example.sn dans le navigateur. Connectez-vous avec les credentials ADMIN_EMAIL et ADMIN_PASSWORD configurés à l’étape 3. Vous arrivez sur le Studio Directus, l’interface admin. Personnalisez immédiatement le profil (avatar, mot de passe à changer). Configurez les paramètres globaux dans Settings → Project Settings (nom du projet, logo, couleurs). Cette personnalisation initiale prend 10-15 minutes.

Étape 7 — Créer première collection

Settings → Data Model → Create Collection. Exemple « Articles » :

  • Field : title (Input, required).
  • Field : slug (Input, unique).
  • Field : content (WYSIWYG).
  • Field : status (Select, options: draft/published).
  • Field : author (Many to One → directus_users).
  • Field : cover_image (Image).

Settings → Data Model → Create Collection. Saisissez le nom (articles par exemple). Directus crée automatiquement les champs id (UUID auto), date_created, date_updated. Ajoutez vos champs métier : title (string), body (markdown), status (dropdown draft/published), published_at (datetime). Cette interface no-code génère la structure SQL Postgres en arrière-plan. Pour modéliser un catalogue de 200 produits, comptez 30-45 minutes pour la structure complète.

Étape 8 — Créer premier item

Sidebar → Articles → Create. Saisir données. Save. Item créé en base et accessible via API.

Cliquez sur la collection nouvellement créée puis Create Item. Remplissez les champs et sauvegardez. Directus enregistre via INSERT INTO articles. Vérifiez en cliquant sur l’item créé : il s’affiche avec les valeurs saisies, les champs date_created et date_updated remplis automatiquement. Cette boucle de feedback rapide est ce qui rend Directus particulièrement adapté aux éditeurs de contenu non-techniques.

Étape 9 — Test API REST

# Token utilisateur depuis Studio
curl -H "Authorization: Bearer YOUR_TOKEN" \
  https://cms.votre-entreprise.com/items/articles
# Doit retourner JSON avec items créés

Directus expose automatiquement une API REST et GraphQL pour chaque collection. Testez avec curl : curl https://cms.example.sn/items/articles?fields=id,title,status&filter[status][_eq]=published&limit=10. La réponse JSON liste les articles publiés. Pour les opérations qui exigent l’authentification (création, modification), utilisez un token Bearer (créé via Settings → Tokens) ou un static_token côté serveur. Cette API consomme côté frontend Next.js, Astro ou React Native sans configuration additionnelle.

Étape 10 — Configurer permissions Public

Settings → Access Control → Public → Articles. Activer Read pour endpoints publics. Test :

curl https://cms.votre-entreprise.com/items/articles
# Doit retourner sans token

Par défaut, l’API Directus refuse l’accès non authentifié. Pour exposer publiquement une collection (catalogue produit accessible sans login), allez dans Settings → Roles → Public → cliquez sur la collection cible. Activez les permissions Read avec les filtres appropriés (par exemple status equals published seulement). Cette discipline empêche d’exposer accidentellement les drafts ou les données sensibles. Auditez régulièrement les permissions Public pour éviter les fuites.

Étape 11 — Sauvegardes

# Cron quotidien
docker exec directus-db pg_dump -U directus directus | gzip > /tmp/dir-pg.gz
mc mirror minio-prod/directus /backup/directus-files/
restic backup /tmp/dir-pg.gz /backup/directus-files
rm /tmp/dir-pg.gz

Trois ressources critiques. Postgres via pg_dump quotidien (collections, items, utilisateurs, permissions). Volume uploads/ via restic ou borg vers Backblaze B2 (fichiers uploadés par les éditeurs). Volume extensions/ si vous installez des extensions personnalisées. Stockez hors VPS principal. Testez la restauration trimestriellement : restaurez Postgres avant les fichiers, sinon les liens entre items et fichiers sont rompus.

Erreurs fréquentes

Erreur Cause Solution
Studio crashes au login SECRET trop court 32 octets base64 minimum
S3 upload échoue Endpoint slash final Sans slash final
API 403 sur public Permissions pas configurées Public role activer Read
Trop lent Cache Redis désactivé CACHE_ENABLED=true
WebSocket déconnecté Caddy pas WS-aware Vérifier reverse_proxy WS
Migration Postgres bloquée Permissions DB User Postgres avec CREATE DATABASE

Ajustements pratiques pour Dakar, Abidjan et Bamako

Trois précisions. Hetzner Storage Box : alternative MinIO pour stocker fichiers volumineux (catalogue produits). 1 To à 3,20 €/mois. Multilingue collection : Directus translations interface gère FR/AR/EN sans duplications. Crucial pour Maroc, Tunisie, Liban. Editor permissions : limiter rédacteurs marketing à édition uniquement, jamais delete. Workflow draft → review → published.

Tutoriels frères

Cette installation Directus se complète bien avec d’autres briques. Astro ou Next.js consomment l’API GraphQL pour générer le frontend statique ou SSR. Algolia ou Meilisearch indexent les items pour la recherche full-text. Authentik OIDC pour le SSO des éditeurs. MinIO ou Backblaze B2 pour stocker les uploads sur S3-compatible (libère le disque local du VPS). Cette stack cohérente forme un CMS moderne maîtrisé.

FAQ

Directus Cloud ou self-hosted ? Self-hosted gratuit complet. Cloud à partir de 99 USD/mois si pas de sysadmin.

Migration depuis Strapi ? Export JSON, scripts custom pour mapping schéma.

Postgres ou MySQL ? Postgres recommandé (FTS natif, pgvector pour IA, plus solide).

Plugins/extensions ? 100+ extensions community + custom Vue components.

Capacité Hetzner CX23 ? 100k items + 50 admins confortable.

Pour explorer plus loin

Pour creuser Directus, consultez la documentation officielle docs.directus.io qui détaille les Flows (automation), les hooks (extensions custom), les permissions granulaires (RBAC + ABAC). Le Discord Directus rassemble une communauté active. Pour les équipes qui veulent apprendre par l’exemple, le repository officiel directus/directus contient un dossier examples/ avec des cas d’usage typiques (e-commerce, blog, multi-tenant SaaS) qui accélèrent la prise en main.

Étape 1 — Préparer un VPS prêt pour Coolify

Coolify v4 demande au minimum 2 vCPU, 2 Go de RAM et 30 Go de SSD pour faire tourner sereinement Postgres 16 plus Directus 11. Pour un projet client basé à Dakar ou Abidjan, un Hetzner CPX21 (4 vCPU, 8 Go, 80 Go NVMe) à 8,40 EUR/mois soit ≈ 5 510 FCFA donne une marge confortable. Évitez les datacenters lointains : préférez Helsinki ou Nuremberg, latence ≈ 80 ms vers Dakar via fibre West Africa Cable System.

ssh root@votre-vps
apt update && apt upgrade -y
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash

L’installation prend 4 à 6 minutes et déploie Coolify dans Docker avec son propre proxy Traefik. La sortie finale affiche l’URL d’accès (http://IP:8000) et un mot de passe initial à changer dès la première connexion.

Étape 2 — Créer le projet et la base Postgres 16

Dans l’interface Coolify, créez un nouveau Project nommé directus-prod, puis ajoutez une ressource Database de type PostgreSQL 16. Coolify provisionne le conteneur, génère un mot de passe fort et expose la base sur le réseau Docker interne uniquement, ce qui évite l’exposition publique du port 5432.

# Variables visibles dans Coolify > Database > Environment
POSTGRES_USER=directus
POSTGRES_PASSWORD=
POSTGRES_DB=directus
# URL interne pour Directus
DB_HOST=postgresql-xxxx
DB_PORT=5432

Activez immédiatement la sauvegarde automatique S3 dans l’onglet Backups, sinon vous risquez la perte de schéma au premier crash. Pointez vers Backblaze B2 ou Wasabi avec rétention de 14 jours minimum.

Étape 3 — Déployer Directus via une ressource Application

On choisit le type de ressource Docker Image dans Coolify et on pointe vers directus/directus:11. Cette image officielle inclut Node 22 LTS, l’API REST, GraphQL, le Data Studio admin et tous les drivers de base nécessaires. Évitez la balise latest en production : elle change sans préavis et peut casser une migration.

# Variables d'environnement Directus
KEY=
SECRET=
DB_CLIENT=pg
DB_HOST=postgresql-xxxx
DB_PORT=5432
DB_DATABASE=directus
DB_USER=directus
DB_PASSWORD=
ADMIN_EMAIL=admin@votredomaine.com
ADMIN_PASSWORD=
PUBLIC_URL=https://cms.votredomaine.com

Coolify détecte le port 8055 exposé par l’image et propose automatiquement un binding vers le proxy Traefik. Cliquez sur Deploy : le premier démarrage prend 30 à 60 secondes le temps que Directus exécute ses migrations initiales sur la base vide.

Étape 4 — Configurer le domaine HTTPS avec Let’s Encrypt

Dans l’onglet Domains de la ressource Directus, ajoutez cms.votredomaine.com. Préalablement, créez un enregistrement DNS de type A chez votre registrar pointant vers l’IP du VPS. Coolify récupère automatiquement un certificat Let’s Encrypt via Traefik, opération qui prend 30 à 90 secondes.

# Vérifier la propagation DNS depuis Dakar
dig cms.votredomaine.com +short
# Tester le certificat
curl -I https://cms.votredomaine.com/server/health

La sortie attendue est HTTP/2 200 avec le JSON {"status":"ok"}. Si le certificat n’est pas généré, vérifiez que le port 80 est ouvert dans le firewall : Let’s Encrypt fait sa validation HTTP-01 sur ce port avant de signer.

Étape 5 — Première collection et permissions publiques

Connectez-vous à https://cms.votredomaine.com/admin avec les identifiants admin. Créez votre première collection nommée articles avec les champs title (string), slug (string unique), body (rich text), cover (file), published_at (datetime). Directus génère immédiatement les endpoints REST et GraphQL correspondants.

# Récupérer les articles publiés via l'API REST
curl https://cms.votredomaine.com/items/articles?filter[published_at][_lte]=$(date -Iseconds)&fields=id,title,slug
# Via GraphQL
curl -X POST https://cms.votredomaine.com/graphql   -H "Content-Type: application/json"   -d '{"query":"{ articles { id title slug } }"}'

Pour autoriser la lecture publique des articles publiés, allez dans Settings > Roles & Permissions > Public, sélectionnez la collection articles, et accordez Read avec un filtre sur published_at non null. C’est le réflexe sécurité minimum pour ne pas exposer les brouillons.

Étape 6 — Stocker les fichiers sur S3 plutôt que sur le VPS

Par défaut Directus écrit les uploads dans /directus/uploads à l’intérieur du conteneur, ce qui sature le VPS et empêche toute mise à l’échelle horizontale. Le réflexe production : externaliser sur S3-compatible. Backblaze B2, Wasabi, Scaleway Object Storage, ou même MinIO auto-hébergé conviennent.

STORAGE_LOCATIONS=s3
STORAGE_S3_DRIVER=s3
STORAGE_S3_KEY=
STORAGE_S3_SECRET=
STORAGE_S3_BUCKET=directus-uploads
STORAGE_S3_REGION=eu-central-003
STORAGE_S3_ENDPOINT=https://s3.eu-central-003.backblazeb2.com

Redémarrez le service. Les nouveaux uploads partent directement sur S3, et Directus génère les URLs signées temporaires à la volée. Coût type pour un blog avec 5 Go d’images : 0,03 USD/mois soit ≈ 20 FCFA, négligeable.

Étape 7 — Sauvegarder Postgres et S3 ensemble

La sauvegarde de Coolify couvre Postgres uniquement. Pour avoir un point de cohérence entre la base et les fichiers, on enchaîne un dump suivi d’une copie S3-vers-S3 dans la même fenêtre temporelle. Un script bash exécuté chaque nuit suffit.

#!/bin/bash
# /opt/scripts/backup-directus.sh
TS=$(date +%Y%m%d-%H%M)
docker exec postgresql-xxxx pg_dump -U directus directus | gzip > /backup/directus-$TS.sql.gz
aws s3 sync s3://directus-uploads s3://directus-backup/uploads-$TS   --endpoint-url https://s3.eu-central-003.backblazeb2.com

Planifiez via systemd timer à 03:00 UTC, soit 03:00 à Dakar et 03:00 à Abidjan (UTC+0). Vérifiez chaque mois la restauration sur un VPS de test, sinon vous découvrirez un problème de format au pire moment.

Étape 8 — Migrer un projet Strapi v4 vers Directus 11

Beaucoup de projets démarrés en 2023-2024 sur Strapi v4 cherchent à migrer suite aux changements de licence Strapi v5 et à la stabilité du modèle Directus. La migration se fait en trois temps : extraction du schéma Strapi, mapping vers les collections Directus, import des données via l’API.

# 1. Export Strapi
curl -H "Authorization: Bearer $STRAPI_TOKEN"   https://strapi.ancien.com/api/articles?populate=* > articles.json

# 2. Création de la collection Directus en miroir (manuelle dans le studio)

# 3. Import via API Directus
jq -c '.data[]' articles.json | while read item; do
  curl -X POST https://cms.votredomaine.com/items/articles     -H "Authorization: Bearer $DIRECTUS_TOKEN"     -H "Content-Type: application/json"     -d "$item"
done

Comptez une demi-journée pour 500 articles avec relations simples, deux jours pour un site complexe avec composants imbriqués. Testez sur un environnement Coolify de staging avant de basculer le DNS production.

Étape 9 — Monitoring et alertes en production

Coolify expose en lecture seule les métriques CPU/RAM par service. Pour étoffer le tableau, ajoutez un Uptime Kuma sur le même VPS (ou mieux, un VPS séparé) qui ping /server/health toutes les 60 secondes. En cas de timeout, alerte Telegram instantanée à l’administrateur d’astreinte.

Voir aussi notre guide notifications Uptime Kuma et notre tutoriel d’installation Coolify pour la mise en place complète du monitoring.

Étape 10 — Coût total et arbitrage face à un SaaS hébergé

Bilan mensuel d’un setup Directus + Coolify pour un site moyen : VPS Hetzner CPX21 à 8,40 EUR (≈ 5 510 FCFA), Backblaze B2 pour 5 Go de fichiers à 0,03 USD (≈ 20 FCFA), domaine .com à 1 EUR/mois (≈ 656 FCFA), soit environ 6 200 FCFA/mois tout compris. Un Directus Cloud équivalent démarre à 99 USD/mois (≈ 64 940 FCFA), soit 10 fois plus cher.

L’arbitrage est clair pour un freelance ou une PME ouest-africaine maîtrisant SSH : auto-héberger via Coolify divise la facture par dix tout en gardant la portabilité totale (Postgres + Docker = standards). Le coût caché reste les 2 à 4 heures mensuelles d’administration que vous facturez à votre client séparément.

Étape 11 — Sécuriser l’admin Directus avec rôles fins

Le rôle Administrator donne accès à tout, y compris la suppression irréversible des collections. En production, créez systématiquement deux rôles supplémentaires : Editor (CRUD sur les collections de contenu uniquement) et Viewer (lecture seule pour les parties prenantes qui veulent consulter sans risque). C’est le minimum pour limiter le rayon d’impact d’un compte compromis.

# Via API Directus, créer un rôle Editor
curl -X POST https://cms.votredomaine.com/roles   -H "Authorization: Bearer $ADMIN_TOKEN"   -H "Content-Type: application/json"   -d '{"name":"Editor","admin_access":false,"app_access":true}'

Pour les comptes humains, activez l’authentification à deux facteurs dans User Directory > [utilisateur] > Two-Factor Authentication. Directus 11 supporte TOTP via Google Authenticator ou Aegis. Comptez 30 secondes par utilisateur pour scanner le QR code et stocker le secret de récupération.

Étape 12 — Cache Redis pour soulager Postgres en lecture

Sur un site qui dépasse 50 000 requêtes API par jour, Postgres commence à montrer des signes de saturation sur les requêtes complexes avec relations multiples. Directus supporte nativement Redis comme cache de réponses. Coolify provisionne un Redis 7 en deux clics.

CACHE_ENABLED=true
CACHE_STORE=redis
CACHE_REDIS=redis://redis-xxxx:6379
CACHE_TTL=15m
CACHE_AUTO_PURGE=true

L’option CACHE_AUTO_PURGE invalide automatiquement le cache quand un item est modifié dans le studio, ce qui évite les surprises éditoriales. Mesurez l’impact : sur un blog avec 1 000 articles publiés, le temps de réponse moyen passe de 80 ms à 8 ms côté API publique.

Étape 13 — Webhooks pour déclencher un build Astro ou Next

Pour un site statique généré par Astro, Next ou SvelteKit qui consomme Directus comme source headless, on configure un webhook qui déclenche le build CI à chaque publication. Settings > Webhooks > Create, méthode POST, URL du déclencheur Gitea Actions ou Netlify Build Hook.

{
  "url": "https://api.netlify.com/build_hooks/abc123",
  "method": "POST",
  "actions": ["create","update","delete"],
  "collections": ["articles","pages"],
  "headers": [{"header":"X-Source","value":"directus"}]
}

Le build Netlify ou Cloudflare Pages prend généralement 1 à 3 minutes pour un site statique de 200 pages. Côté éditorial, le rédacteur publie dans Directus puis voit le résultat live moins de 5 minutes plus tard. C’est la souplesse d’un CMS classique avec la performance d’un site statique servi en CDN.

Partager