Chapitre 9.5 — Zéro-downtime & logs
⏱️ TL;DR — Déployer une nouvelle version ne doit pas afficher une page blanche à tes visiteurs. Le principe du zéro-downtime : builder à côté (l’ancienne version continue de servir), puis basculer en une opération rapide. Sur un service Node,
systemctl restartcoupe une poignée de secondes — souvent acceptable ; le vrai zéro-coupure demande soit le cluster reload de PM2, soit des releases par symlink (la technique industrielle, détaillée en Partie 13). On branche un healthcheck (/api/health) pour vérifier qu’une version est vivante avant de basculer, on apprend à lire et faire tourner les logs (journald), et on tranche enfin, sans dogme, quand un VPS bat Vercel pour du Next.js — et quand c’est l’inverse.
🎯 Objectifs
- Comprendre le principe du zéro-downtime : builder à côté, basculer vite.
- Savoir ce que valent
reloadvsrestartpour un service Node. - Brancher un healthcheck applicatif et s’en servir avant de basculer.
- Lire, filtrer et faire tourner les logs applicatifs (journald, rotation).
- Décider quand héberger sur VPS plutôt que sur Vercel (et l’inverse).
- Dérouler une checklist de déploiement Next manuel fiable.
Le problème : déployer sans page blanche
Le déploiement naïf est brutal : tu git pull, tu npm run build dans le dossier en cours d’exécution, puis tu redémarres. Pendant tout le build (dizaines de secondes à minutes) et le redémarrage, ton site est cassé ou down. Pire : si le build échoue à mi-chemin, tu as un .next/ corrompu et un site mort, sans version saine sous la main.
Le principe du zéro-downtime inverse l’ordre : on prépare la nouvelle version à côté, pendant que l’ancienne sert toujours, et on ne bascule que lorsque la nouvelle est prête et vérifiée. Trois approches, de la plus simple à la plus robuste.
Approche 1 — Build à côté puis restart
La version simple, sans outillage : tu build dans un dossier neuf, et tu ne touches au service qu’une fois le build réussi.
# L'ancienne version tourne toujours et sert les visiteurs pendant tout ca
cd /var/www/formacampus
git pull origin main # recupere la nouvelle version
npm ci # deps exactes
npm run build # BUILD : si ca echoue, on s'arrete AVANT de toucher au service
# -> build OK ? seulement maintenant on bascule :
sudo systemctl restart formacampus-webLe point clé : le restart n’arrive qu’après un build réussi. Si npm run build plante, tu n’as pas redémarré : l’ancienne version tourne encore, ton site est toujours debout. La coupure se réduit aux quelques secondes du redémarrage de Node (le temps que le nouveau process démarre et réécoute sur 3000).
💡 Réflexe — Ne redémarre jamais avant d’avoir un build vert. Enchaîne
npm ci && npm run build && sudo systemctl restart formacampus-webavec des&&: si une étape échoue, les suivantes ne s’exécutent pas, et le service n’est pas touché. C’est la version pauvre mais efficace du « on ne bascule que si c’est prêt ».
reload vs restart pour un service Node
Sur Nginx, systemctl reload recharge la config sans couper — magique. Sur un service Node, c’est différent, et il faut être honnête.
restart= stop puis start. Le process meurt, un nouveau démarre. Il y a une brève fenêtre (secondes) où l’app ne répond pas. Pendant cette fenêtre, Nginx renvoie 502 aux requêtes qui tombent pile là.reload= envoyer un signal pour recharger sans redémarrer. Mais une app Node ne sait pas recharger son code à chaud par défaut : il n’y a pas d’ExecReloadutile pour unnext startstandard.reloadsur un service Node basique ne fait donc rien d’utile (voire échoue si aucunExecReloadn’est défini).
Conclusion sans détour : pour un service systemd Node classique, c’est restart, avec sa micro-coupure. Le vrai zéro-coupure vient d’ailleurs :
- PM2 cluster (
pm2 reload) : redémarre les instances une par une, il y en a toujours une qui répond (chapitre 9.3). - Releases par symlink : deux versions coexistent, Nginx (ou un lien
current) bascule de l’une à l’autre de façon atomique — la technique industrielle, montée en Partie 13.
⚠️ Piège — Croire que
systemctl reload formacampus-webdéploie ta nouvelle version « sans coupure ». Pour un service Node standard,reloadne recharge pas le code de l’app (Node ne gère pas ça tout seul) : au mieux il ne fait rien, au pire il échoue. Ne te fie pas à unreloadsilencieux pour penser que c’est déployé — vérifie que la nouvelle version répond vraiment (voir le healthcheck ci-dessous). Pour Node, le déploiement passe parrestartou par une bascule de release.
Approche 2 — Releases par symlink (l’atomique)
La technique pro (façon Capistrano/Deployer) : chaque déploiement crée un dossier daté, on y build, puis on fait pointer un lien symbolique current vers cette nouvelle release — en une opération atomique. Le service tourne sur current. Le rollback est instantané : on repointe current vers la release précédente.
# Schema (detaille en Partie 13)
/var/www/formacampus/
releases/
2026-07-15_1430/ <- nouvelle version, buildee ici
2026-07-14_0900/ <- version precedente, gardee pour rollback
current -> releases/2026-07-15_1430 <- le lien que le service suitOn construit tranquillement dans le nouveau dossier (l’ancien sert toujours), puis ln -sfn bascule current d’un coup, et un restart rapide fait repartir Node sur la nouvelle release. Rollback = repointer le lien et redémarrer. On industrialise ça en Partie 13 — CI/CD ; ici, retiens le principe : préparer à côté, basculer atomiquement, garder l’ancienne pour revenir en arrière.
Le healthcheck : savoir qu’une version est vivante
Comment être sûr que la nouvelle version tourne avant de l’annoncer prête ? On expose une route healthcheck minuscule qui répond « je suis en vie ». Par convention, /api/health.
// Une route Next (app/api/health/route.ts) qui renvoie un statut simple
// GET /api/health -> 200 {"status":"ok"}
{
"status": "ok"
}Après un déploiement, on teste la route avant de considérer que c’est bon :
# Attendre que la nouvelle version reponde 200 sur son healthcheck
curl -fsS http://127.0.0.1:3000/api/health && echo " <- app en vie"
# -f : echoue si code HTTP >= 400 | -s : silencieux | -S : montre l'erreurLe healthcheck sert trois publics : ton script de déploiement (ne bascule que si /api/health répond 200), ton monitoring (Partie 14, qui pingue cette route et t’alerte si elle tombe), et un futur load balancer (Partie 15, qui retire une instance malade du rotation).
💡 Réflexe — Fais un healthcheck honnête mais léger. S’il se contente de renvoyer
200 {"status":"ok"}, il dit juste « le process Node répond ». Si tu veux qu’il atteste que l’app est réellement opérationnelle, fais-lui vérifier une dépendance critique (unSELECT 1sur la base). Mais garde-le rapide et sans effet de bord : il est appelé souvent (monitoring, LB), il ne doit ni ramer ni écrire quoi que ce soit.
Lire et gérer les logs applicatifs
En service systemd, tout ce que ton app écrit sur la sortie standard (console.log, console.error) est capturé par journald. Pas besoin de fichier de log à gérer toi-même : journalctl est ta fenêtre.
journalctl -u formacampus-web -f # suivre en direct (Ctrl+C pour sortir)
journalctl -u formacampus-web -n 200 --no-pager # les 200 dernieres lignes
journalctl -u formacampus-web --since "1 hour ago" # filtre par temps
journalctl -u formacampus-web -p err # seulement les erreurs (priorite >= err)
journalctl -u formacampus-web --since today | grep -i "500" # chercher un motifLa rotation : journald plafonne tout seul la taille de ses journaux (configurable dans journald.conf via SystemMaxUse) — tu n’as pas de disque qui se remplit sans fin, contrairement à un fichier .log naïf. Si ton app écrit aussi dans un fichier (/var/log/formacampus/…), là tu dois gérer la rotation avec logrotate, sinon le disque se remplit. On approfondit l’observabilité (métriques, alerting, agrégation) en Partie 14.
⚠️ Piège — Une app trop bavarde (
console.logà chaque requête) noie les vraies erreurs et remplit les journaux. En prod, logue utile : les erreurs et les événements importants, pas chaque détail. Et si tu écris dans un fichier en plus de la sortie standard, branche logrotate dès le début — un/var/log/…sans rotation finit toujours par saturer le disque et faire tomber la machine, parfois des mois plus tard, au pire moment.
🔒 Sécurité — Attention à ce que tu logues. Un
console.log(user)qui déverse un objet entier peut cracher des données personnelles (RGPD) ou des secrets (token, mot de passe) dans les journaux — lisibles par quiconque a accès au serveur, et conservés. Ne jamais loguer un mot de passe, un token, un numéro de carte ni un profil complet. Logue des identifiants (userId), pas des contenus sensibles.
VPS ou Vercel pour du Next.js ? Le rappel nuancé
Next.js est fait par Vercel, et Vercel héberge Next admirablement. Alors pourquoi ce cours ? Parce que le choix dépend du contexte, pas d’une religion.
| Situation | Plutôt… | Pourquoi |
|---|---|---|
| Prototype, side-project, trafic faible | Vercel | Déploiement en un git push, zéro admin, généreux gratuitement au début. |
| Besoin de services longue durée (workers, cron, WebSocket persistant) | VPS | Un PaaS serverless coupe les process ; un VPS les fait tourner en continu. |
| Données sensibles à garder en UE, contrôle RGPD strict | VPS | Tu choisis le datacenter et maîtrises où vivent les données. |
| Facture qui explose avec le trafic / le SSR | VPS | Un VPS à prix fixe bat un PaaS facturé à l’usage quand ça monte. |
| Front + API + base sur la même machine | VPS | Tout au même endroit, latence interne nulle, un seul serveur à opérer. |
| Zéro envie d’administrer un serveur | Vercel | Tu paies pour ne pas avoir à faire ce cours — c’est un choix valide. |
La vérité : les deux sont légitimes. Vercel achète du confort ; un VPS achète du contrôle et un coût maîtrisé, au prix de l’administration — celle que ce cours t’apprend. Beaucoup d’équipes commencent sur Vercel et rapatrient sur VPS quand le coût, le RGPD ou les services longue durée l’exigent : c’est exactement le parcours de FormaCampus.
🧭 Sur FormaCampus — FormaCampus quitte son PaaS pour un VPS Ubuntu : facture qui grimpait avec le SSR, données d’apprenants à garder en UE (RGPD), et besoin de workers longue durée (envoi de mails, génération de certificats PDF) qu’un serverless coupait. Son déploiement type aujourd’hui :
git pull && npm ci && npm run build,curldu/api/healthpour vérifier, puissystemctl restart formacampus-web— coupure de deux secondes, absorbée par leRestartet les retries. Bientôt, la Partie 13 rendra tout ça automatique (GitHub Actions + releases par symlink) et zéro-coupure.
Checklist de déploiement Next manuel
Le pense-bête à dérouler à chaque déploiement, tant que ce n’est pas automatisé :
- Code à jour :
git pull origin mainsur la branche de prod. - Deps :
npm ci(jamaisinstallen prod). - Env : les variables/secrets sont en place (
.env.productionhors git) ; rappel :NEXT_PUBLIC_*sont figées au build. - Build :
npm run build— et on s’arrête là s’il échoue (le site tourne encore). - Bascule :
systemctl restart formacampus-web(ou bascule de symlink). - Vérif :
curl -f http://127.0.0.1:3000/api/healthrépond 200, etsystemctl statusestactive (running). - Logs : un œil sur
journalctl -u formacampus-web -fpour repérer une erreur au démarrage. - Public :
curl -I https://formacampus.fren 200 depuis l’extérieur — le vrai test utilisateur.
🐚 Au terminal — Le déploiement complet en une ligne enchaînée, sûre (chaque
&&ne passe que si le précédent réussit) :
cd /var/www/formacampus \
&& git pull origin main \
&& npm ci \
&& npm run build \
&& sudo systemctl restart formacampus-web \
&& sleep 3 \
&& curl -fsS http://127.0.0.1:3000/api/health && echo " <- deploiement OK"📚 La doc — Pour la rotation de fichiers de log :
man logrotate. Pour journald :man journalctletman journald.conf(dontSystemMaxUse). Le déploiement par releases atomiques et l’automatisation complète (build en CI, bascule sans coupure, rollback) sont le sujet de la Partie 13 — CI/CD ; la supervision (alerting sur le healthcheck) celui de la Partie 14.
✏️ Exercices
Exercice 1 — Le déploiement casse le site. Un collègue déploie ainsi : git pull, puis npm run build dans le dossier live, et le build échoue à mi-parcours — le site est maintenant down avec un .next/ corrompu. Qu’aurait-il fallu faire pour que le site reste debout ?
✅ Solution
Le problème est d’avoir buildé dans le dossier en cours d’exécution et de n’avoir aucune version saine de repli. Il fallait préparer à côté et ne basculer qu’après un build réussi : soit enchaîner npm ci && npm run build && systemctl restart avec des && (le restart ne se fait pas si le build échoue, donc l’ancienne version continue de tourner), soit builder dans un dossier de release neuf et basculer un symlink current seulement une fois le build vert (rollback instantané en prime). Principe : on ne touche au live que quand la nouvelle version est prête.
Exercice 2 — reload ou restart ? Pour appliquer une nouvelle version de code sur un service systemd Node classique (next start), quelle commande, et pourquoi pas l’autre ?
✅ Solution
restart. Une app Node standard ne sait pas recharger son code à chaud : systemctl reload ne définit pas d’ExecReload utile pour next start — au mieux il ne fait rien, au pire il échoue. restart (stop + start) recharge bien le nouveau code, au prix d’une micro-coupure de quelques secondes (que Restart=always et les retries Nginx absorbent). Le vrai zéro-coupure vient d’ailleurs : pm2 reload en cluster, ou une bascule de release par symlink.
Exercice 3 — VPS ou Vercel ? Une équipe a une app Next avec SSR intensif, une facture PaaS qui explose, des données d’utilisateurs à garder en UE, et un worker d’envoi d’e-mails longue durée. Recommande, avec trois raisons.
✅ Solution
VPS, pour trois raisons qui se cumulent ici : (1) la facture — un VPS à prix fixe bat un PaaS facturé à l’usage dès que le SSR et le trafic montent ; (2) le RGPD — sur un VPS on choisit le datacenter (UE) et on maîtrise où vivent les données ; (3) le worker longue durée — un serverless coupe les process, un VPS fait tourner un worker en continu (service systemd). C’est le profil type qui justifie de rapatrier sur VPS — et c’est le parcours de FormaCampus. (Vercel resterait pertinent pour un prototype ou une équipe sans envie d’administrer.)
🧠 Quiz de révision
1. Quel est le principe du déploiement zéro-downtime ?
Préparer la nouvelle version à côté pendant que l’ancienne sert toujours, et ne basculer que lorsque la nouvelle est prête et vérifiée. Concrètement : builder dans un dossier neuf (ou au moins ne redémarrer qu’après un build réussi), tester le healthcheck, puis basculer vite (restart rapide, ou symlink atomique). On ne touche jamais au live tant que la nouvelle version n’est pas bonne.
2. Pourquoi reload ne suffit-il pas pour déployer un service Node ?
reload ne suffit-il pas pour déployer un service Node ?Parce qu’une app Node ne recharge pas son code à chaud toute seule : systemctl reload n’a pas d’ExecReload utile pour un next start standard (au mieux il ne fait rien). Pour appliquer du nouveau code, c’est restart (avec une micro-coupure), ou un mécanisme dédié (PM2 cluster, releases par symlink) pour le vrai zéro-coupure.
3. À quoi sert une route healthcheck comme /api/health ?
/api/health ?À attester qu’une version est vivante et répond. Elle sert le script de déploiement (ne basculer que si elle renvoie 200), le monitoring (alerte si elle tombe) et un futur load balancer (retirer une instance malade). On la garde rapide et sans effet de bord ; elle peut vérifier une dépendance critique (un SELECT 1).
4. Où vont les logs d’un service Node, et quel risque avec un fichier de log ?
Sous systemd, la sortie standard de l’app (console.log) est capturée par journald, consultable via journalctl -u <service> — et journald plafonne tout seul sa taille. Si l’app écrit aussi dans un fichier (/var/log/…), c’est à toi de brancher logrotate, sinon le disque se remplit et la machine finit par tomber.
5. Cite deux situations où un VPS bat Vercel pour du Next.js.
Par exemple : (1) le besoin de services longue durée (workers, cron, WebSocket persistant) qu’un serverless coupe ; (2) une facture qui explose avec le SSR/trafic, là où un VPS à prix fixe reste économique. Autres cas valides : contrôle RGPD (choisir le datacenter UE), ou front + API + base sur une seule machine. À l’inverse, Vercel reste idéal pour un prototype ou une équipe sans envie d’administrer.
Fin de la Partie 9. Ton front Next.js tourne en production, en service, en HTTPS, et tu sais le redéployer. On applique la même rigueur aux autres stacks : Partie 10 — Autres stacks (PHP, Symfony, WP…), à commencer par l’API Symfony de FormaCampus.