Déploiement
Déploiement
Ce qu'un environnement de production exécute en plus (jamais à la place) de ce qu'un
environnement de développement fait déjà — vérifié contre Commander::optimize(),
ConfigCache, RouteCache et preload.php.
niang optimize
Une seule commande enchaîne le cache de routes et le cache de configuration :
./bin/niang optimize
Elle affiche aussi, sans les exécuter elle-même (ce sont des réglages serveur, pas des actions CLI ponctuelles) :
Pensez aussi, en production : composer install --no-dev --optimize-autoloader
et activez opcache.validate_timestamps=0 dans votre php.ini.
preload.php existe à la racine du projet : un réglage serveur (opcache.preload dans
php.ini), pas quelque chose qu'une commande CLI ponctuelle comme celle-ci peut activer.
Cache de routes
Voir Routing pour le détail — en résumé, précompile routes/web.php pour éviter de le ré-exécuter à chaque requête :
./bin/niang route:cache
./bin/niang route:clear # pour revenir en arrière
Seules les routes dont l'action est [Controleur::class, 'methode'] survivent au cache — une action en closure est silencieusement ignorée (comptée et signalée à l'écran).
Cache de configuration
Fige tous les config/*.php (et les env() qu'ils contiennent) dans un seul fichier, storage/framework/config.php :
./bin/niang config:cache
./bin/niang config:clear
Une fois config:cache lancé, modifier .env ou un fichier
config/*.php n'a plus aucun effet tant que le cache n'est pas
vidé (Config::load() lit le cache en priorité, sans même regarder les fichiers
source) — à réserver à un déploiement figé, jamais actif en développement.
OPcache et préchargement
preload.php, à la racine du projet, précompile tous les fichiers du framework (vendor/niangpro/framework/packages/)
avec opcache_compile_file() — construit par glob(), jamais une liste
codée en dur. C'est un réglage serveur, activé dans php.ini, pas
quelque chose qu'une commande CLI ponctuelle peut déclencher (le fichier n'a d'effet que
chargé par OPcache lui-même au démarrage de PHP-FPM) :
opcache.preload=/chemin/absolu/vers/le/projet/preload.php
opcache.preload_user=www-data
opcache.validate_timestamps=0
opcache.validate_timestamps=0 arrête de comparer l'horodatage des fichiers à
chaque requête (gain de performance), mais signifie aussi qu'un fichier modifié sur disque
n'est pas repris tant qu'OPcache n'est pas rechargé — à réserver à un
environnement où le déploiement recharge PHP-FPM à chaque mise à jour du code.
Variables d'environnement de production
| Variable | Valeur en production |
|---|---|
APP_ENV | production |
APP_DEBUG | false — sinon la Debug Toolbar s'affiche aux visiteurs et les erreurs exposent leur détail complet. |
APP_KEY | générée une fois (./bin/niang key:generate) et gardée stable — elle signe les URLs (UrlSignature), les jetons API (ApiToken) et les cookies. |
DB_CONNECTION, DB_HOST, DB_DATABASE, DB_USERNAME, DB_PASSWORD | mysql ou pgsql plutôt que sqlite dans la grande majorité des déploiements — voir grammaire multi-SGBD. |
DB_READ_HOST (etc.) | optionnel — un réplica de lecture, voir séparer lecture et écriture. |
MAIL_MAILER | smtp, avec MAIL_HOST, MAIL_FROM_ADDRESS et les identifiants — ni log ni array n'envoient réellement d'email (niang doctor le signale). Voir SMTP. |
Debug et configuration en production
Avec APP_ENV=production, le mode debug est toujours désactivé (pas de trace d'erreur,
pas de barre de debug), même si APP_DEBUG=true traîne dans .env. Aucune requête n'est
servie sans APP_KEY valide : réponse 503, détail dans les logs. Hors production, le debug est actif
sauf APP_DEBUG=false.
Plusieurs serveurs web
Derrière un répartiteur de charge, chaque serveur garde ses fichiers pour lui : un visiteur envoyé sur un autre serveur perd sa session (déconnecté, panier vide), le cache n'est pas partagé, et une limite « 10 tentatives de connexion par minute » devient 10 × le nombre de serveurs. Sessions, cache et limitation de débit passent en base avec deux variables :
SESSION_DRIVER=database
CACHE_DRIVER=database
./bin/niang migrate # tables sessions, cache_entries, rate_limits
./bin/niang doctor # signale une table manquante
file reste le défaut. CACHE_DRIVER pilote aussi la
limitation de débit, dont l'incrément reste atomique en base (un
seul UPDATE conditionnel). Aucun verrou par session : si deux requêtes simultanées du même
visiteur modifient toutes deux la session, la dernière écriture l'emporte.
Avec un serveur Redis, redis remplace database pour les deux variables (et pour QUEUE_DRIVER) : voir Redis.
Mode maintenance
./bin/niang down # toute requête reçoit une page 503
./bin/niang down --retry=60 # + en-tête Retry-After: 60
./bin/niang down --secret # affiche une URL secrète qui vous laisse naviguer (cookie 12 h)
./bin/niang up # rouvre le site
/up et /health restent accessibles, pour que la supervision ne déclenche pas
d'alerte pendant une maintenance prévue. Une API reçoit une 503 en JSON. La page 503
(resources/views/errors/503.php) n'utilise pas le layout du site, qui pourrait dépendre
d'une base en cours de migration. Seul le hachage du secret est écrit sur le disque
(storage/framework/down) ; niang doctor signale un site resté en maintenance.
Avec plusieurs serveurs web, lancez down et up sur chacun.
Performances mesurées
php -d opcache.enable_cli=1 benchmarks/run.php
php -d opcache.enable_cli=1 benchmarks/run.php --markdown # met à jour benchmarks/RESULTS.md
Chaque couche (routage, conteneur, base, vues, requête complète, démarrage) est comparée à son équivalent en PHP natif : p50, p95, p99, débit et mémoire, avec les conditions de mesure. Sur un Apple M1 avec OPcache, une requête complète coûte environ 0,015 ms de framework, plus 0,08 ms de démarrage.
Docker
docker compose up -d # PHP-FPM 8.3 + Nginx + MySQL 8.4 sur http://localhost:8080
docker compose exec app php bin/niang db:seed
docker compose down # -v pour supprimer aussi la base
Au premier démarrage, le conteneur app installe vendor/, crée .env et
une APP_KEY jamais remplacée ensuite, puis migre (NIANGPRO_MIGRATE=false pour
s'en passer). Le code est monté depuis votre dossier. Les variables de compose.yaml
(DB_HOST=db...) priment sur .env, et PHP-FPM les reçoit bien
(clear_env = no). Nginx ne sert que public/, n'exécute que
index.php et refuse les fichiers cachés. Autre port :
NIANGPRO_HTTP_PORT=8000 docker compose up -d.
Pour la production, docker/php/Dockerfile construit une image autonome (code copié,
composer install --no-dev) : passez APP_KEY, APP_ENV=production,
APP_DEBUG=false et les DB_* en variables d'environnement, et mettez
opcache.validate_timestamps=0 dans docker/php/php.ini. Docker reste facultatif :
un hébergement PHP classique suffit.
Sauvegardes
Le framework ne fournit pas de commande de sauvegarde : les outils de chaque moteur font mieux. Trois choses sont à sauvegarder, et une sauvegarde n'est fiable que si sa restauration a été essayée.
Base de données
# SQLite : copie cohérente, même pendant des écritures (ne copiez pas le fichier avec cp)
sqlite3 storage/database.sqlite ".backup 'backups/db-$(date +%F).sqlite'"
# MySQL / MariaDB
mysqldump --single-transaction --routines --triggers --no-tablespaces \
--set-gtid-purged=OFF -u niangpro -p niangpro | gzip > backups/db-$(date +%F).sql.gz
# restauration
gunzip -c backups/db-2026-09-27.sql.gz | mysql -u niangpro -p niangpro
# PostgreSQL
pg_dump -Fc -U niangpro niangpro > backups/db-$(date +%F).dump
pg_restore --clean --if-exists -U niangpro -d niangpro backups/db-2026-09-27.dump
--single-transaction donne une image cohérente sans bloquer les écritures (tables InnoDB).
--set-gtid-purged=OFF est nécessaire avec MySQL quand les GTID sont activés (réplication, services
gérés) : sans lui, la restauration échoue sur un serveur qui a déjà un historique ; retirez l'option avec
MariaDB, qui ne la connaît pas.
Fichiers
tar -czf backups/storage-$(date +%F).tgz --exclude='storage/framework' --exclude='storage/logs' storage
storage/app/ contient les fichiers téléversés ; storage/framework/ (cache, vues
compilées, sessions fichier) se reconstruit. Avec SQLite, la base est aussi dans storage/ :
sauvegardez-la avec .backup, pas par l'archive. Sur un disque S3,
activez plutôt le versionnement du bucket (ou une réplication vers un second bucket).
Configuration et clé
Le code est dans git ; le fichier .env ne l'est pas. Conservez-le dans un coffre (gestionnaire de
secrets, gestionnaire de mots de passe de l'équipe), jamais à côté des sauvegardes de la base.
APP_KEY doit survivre à tout : sans elle, les secrets de
double authentification et toutes les données chiffrées
avec Crypt sont perdus, et les cookies chiffrés sont invalidés. Restaurer une base avec une autre clé
ne suffit pas.
Automatiser
# crontab : chaque nuit à 2 h (backup.sh regroupe les commandes ci-dessus),
# puis suppression des sauvegardes de plus de 14 jours
0 2 * * * cd /var/www/app && ./scripts/backup.sh && find backups -mtime +14 -delete
Copiez ensuite les sauvegardes hors du serveur (autre machine, stockage objet d'un autre fournisseur) : une sauvegarde restée sur le disque qu'elle protège disparaît avec lui. Chiffrez-les si elles quittent votre infrastructure, et restaurez-en une régulièrement sur une machine de test.
Vérifier avant/après déploiement
./bin/niang doctor # environnement statique : PHP, extensions, .env, permissions — avant de démarrer
./bin/niang health # état d'exécution réel : DB, cache, storage, queue — après démarrage
doctor quitte avec un code non nul si une vérification critique échoue
(utilisable en niang doctor || exit 1 dans un script de déploiement) et avertit
(sans faire échouer) si APP_ENV=production et APP_DEBUG=true sont
actifs en même temps. Voir HealthCheck pour le
détail de ce que chacun vérifie réellement.