Skip to Content
Serveur LinuxPartie 16 — Cookbook & Annexes16.3 — Checklist de mise en prod

Chapitre 16.3 — Checklist de mise en prod

⏱️ TL;DR — La liste complète à dérouler avant, pendant et après un go-live, pour ne laisser aucun trou. Elle rassemble tout le cours en cases à cocher, par domaine : serveur durci, réseau/DNS, web + HTTPS, application en service, base non exposée, sauvegardes testées, observabilité, déploiement réversible, revue de sécurité finale. Coche au fur et à mesure. Le jour où tu ouvres au public, chaque case décochée est un incident en puissance.

🎯 Objectifs

  • Mettre en prod sans rien oublier : sécurité, HTTPS, redémarrage auto, sauvegardes, monitoring.
  • Distinguer ce qui doit être fait avant d’ouvrir au public de ce qui se vérifie après.
  • Refuser le « ça marche donc c’est bon » : un site accessible n’est pas un site en prod.

Comment utiliser cette checklist

Déroule-la dans l’ordre, de haut en bas — elle suit la logique du cours (on empile les couches). Adapte à ton projet (tout ne s’applique pas partout), mais justifie chaque case que tu sautes. Les cases marquées (bloquant) ne se négocient pas avant d’ouvrir au public.

⚠️ Piège — Le plus grand piège du go-live : croire qu’un site joignable est un site en prod. Sans HTTPS, sans redémarrage auto, sans pare-feu, sans sauvegarde testée, sans monitoring, c’est une panne ou une fuite qui attend son heure. Cette liste existe pour transformer « ça tourne » en « ça tient ».

🖥️ Serveur

  • Système à jour : sudo apt update && sudo apt full-upgrade (bloquant)
  • Utilisateur non-root de travail créé (ex. deploy), avec sudo (bloquant)
  • Connexion root en SSH désactivée (PermitRootLogin no) (bloquant)
  • SSH par clés uniquement (PasswordAuthentication no), clé testée dans un 2e terminal (bloquant)
  • sshd_config durci : AllowUsers deploy, éventuellement Port non standard ; sudo sshd -t OK
  • Pare-feu UFW actif : default deny incoming, seuls SSH + 80/443 ouverts (bloquant)
  • fail2ban installé et actif (protège SSH du bruteforce)
  • Mises à jour de sécurité automatiques (unattended-upgrades) activées
  • Swap en place (1–2 Go sur un petit VPS) et persistant dans /etc/fstab
  • Fuseau horaire et NTP réglés (timedatectl) — logs et cron à la bonne heure
  • Hostname propre (hostnamectl)

🌐 Réseau & DNS

  • Enregistrement A (et AAAA si IPv6) pointant vers l’IP du VPS (bloquant)
  • www géré (redirection ou enregistrement dédié)
  • Propagation DNS vérifiée : dig +short formacampus.fr renvoie la bonne IP
  • Seuls les ports nécessaires sont ouverts (vérifié depuis l’extérieur, pas juste ufw status)
  • Port 80 ouvert (nécessaire au challenge Certbot, même si tout est en HTTPS)
  • Reverse DNS / SPF-DKIM-DMARC si le serveur envoie des mails (sinon spam)

🔒 Web & HTTPS

  • Nginx installé, sudo nginx -t passe (bloquant)
  • Server block configuré, site activé (symlink dans sites-enabled/)
  • HTTPS actif via Let’s Encrypt (certbot --nginx) (bloquant)
  • Renouvellement automatique du certificat vérifié : certbot renew --dry-run OK (bloquant)
  • Redirection HTTP → HTTPS en place (301) (bloquant)
  • HSTS activé (Strict-Transport-Security)
  • En-têtes de sécurité posés (X-Content-Type-Options, X-Frame-Options / CSP, Referrer-Policy)
  • Compression gzip (ou brotli) activée
  • client_max_body_size réglé sur la taille max légitime des uploads
  • Note SSL Labs vérifiée (viser A / A+), pas de TLS obsolète
  • La bannière de version Nginx masquée (server_tokens off)

⚙️ Application

  • Build de production (pas le mode dev) : NODE_ENV=production, next build, cache Symfony réchauffé (bloquant)
  • App lancée en service systemd, avec enable (survit au reboot) (bloquant)
  • Restart=always (ou on-failure) : l’app remonte après un crash (bloquant)
  • App qui écoute en local (127.0.0.1:3000), jamais exposée directement (bloquant)
  • App tourne sous un utilisateur non-root (moindre privilège) (bloquant)
  • Variables d’env / secrets hors du dépôt git, dans un .env en 600 (bloquant)
  • Migrations de base jouées, dans le bon ordre du déploiement
  • curl -I http://127.0.0.1:3000 répond 200 (l’app va bien, indépendamment de Nginx)
  • Health-check applicatif exposé (ex. /health) pour le monitoring

🗄️ Base de données

  • Base non exposée à Internet : écoute en local, pg_hba.conf restrictif (bloquant)
  • Port DB (5432 / 3306) fermé au pare-feu / non routé (bloquant)
  • Utilisateur dédié à l’app (pas le superuser), droits limités à sa base (bloquant)
  • Mot de passe fort et unique, stocké hors git (bloquant)
  • Encodage / locale corrects (UTF-8)
  • Réglages mémoire de base adaptés à la RAM du VPS

💾 Sauvegardes

  • Sauvegardes automatisées (cron / timer), pas « à la main quand j’y pense » (bloquant)
  • Règle 3-2-1 respectée : 3 copies, 2 supports, 1 hors-site (bloquant)
  • La base est dumpée régulièrement (pg_dump / mysqldump), pas juste les fichiers
  • Sauvegardes chiffrées (restic / borg) — surtout l’offsite (bloquant)
  • Restauration TESTÉE de bout en bout — une sauvegarde non testée n’existe pas (bloquant)
  • Rétention définie (ex. 7 quotidiennes, 4 hebdo, 6 mensuelles) et purge automatique
  • Runbook de reprise écrit : comment reconstruire le serveur de zéro

📈 Observabilité

  • Logs centralisés et consultables (journalctl, /var/log)
  • logrotate configuré (logs applicatifs + Nginx) — sinon disque plein garanti (bloquant)
  • Logs Docker plafonnés (max-size) si Docker
  • Monitoring d’uptime externe (Uptime Kuma, healthchecks) qui teste le site de l’extérieur (bloquant)
  • Alertes configurées et reçues (test réel) sur : site down, disque à plus de 80 %, certificat proche expiration (bloquant)
  • Métriques machine surveillées (CPU, RAM, disque, load)
  • Supervision applicative des erreurs (ex. Sentry) si pertinent

🚀 Déploiement

  • Déploiement reproductible : un deploy.sh (set -euo pipefail) ou un pipeline CI/CD (bloquant)
  • Build/test en CI (pas sur le petit VPS) pour éviter l’OOM au build
  • Déploiement par releases + symlink current (ou équivalent) pour un rollback instantané (bloquant)
  • Procédure de rollback testée (revenir à la release précédente en une commande)
  • Anciennes releases purgées automatiquement (sinon disque plein)
  • Secrets CI dans le coffre du fournisseur (GitHub Actions secrets), jamais en clair
  • Déploiement sans downtime vérifié (health-check avant bascule)

🛡️ Sécurité — revue finale

  • sudo ss -tulpn : aucun service inattendu n’écoute sur 0.0.0.0 (bloquant)
  • Aucun secret dans le dépôt git (historique compris) (bloquant)
  • Permissions saines : secrets en 600, pas de 777, bon propriétaire partout
  • Pages/outils d’admin protégés (auth, IP allow-list) ou non exposés
  • fail2ban et UFW confirmés actifs après le dernier reboot
  • Un reboot de test effectué : tout remonte tout seul (services enable, swap, pare-feu) (bloquant)
  • Accès de secours connu : console du fournisseur testée (au cas où SSH lâche)

🔒 Sécurité — La ligne qui résume la revue finale : rien qui écoute sur 0.0.0.0 sauf ce qui doit l’être (Nginx sur 80/443, SSH). Une base, un cache, un dashboard qui traînent exposés sont la faille la plus courante. sudo ss -tulpn te le dit en une commande — fais-le avant d’annoncer le go-live.

🧭 Sur FormaCampus — Avant d’ouvrir formacampus.fr au public, l’équipe déroule toute cette liste : VPS durci, DNS pointé, front formacampus-web et API en services enable, Nginx + HTTPS auto-renouvelé, Postgres injoignable de l’extérieur, sauvegardes restaurées pour de vrai sur un serveur de test, Uptime Kuma qui alerte, deploy.sh avec rollback. Puis un reboot de contrôle : tout remonte seul. Alors seulement, on annonce.

💡 Réflexe — Fais de cette checklist un fichier vivant dans ton dépôt (GO-LIVE.md). À chaque nouveau projet, tu la copies, tu coches, tu l’adaptes. Une checklist qu’on rejoue est une checklist qui s’améliore à chaque incident évité.


Il te reste le vocabulaire, un dernier quiz de synthèse et la suite du voyage : 16.4 — Glossaire, quiz & suite.

Last updated on