E-NO
Production Nginx 7 min de lecture

Checklist d'exploitation Nginx en production : de l'observation au résultat vérifié

calendar_today Publié : 2026-08-08
update Dernière mise à jour : 2026-08-08
analytics Efficacité SEO : 97%
Illustration du guide technique pour « Checklist d'exploitation Nginx en production : de l'observation au résultat vérifié ».

Cette checklist fournit un cadre opérationnel prêt pour la production, couvrant l'inventaire, les changements de configuration sûrs, la vérification, les modes de défaillance avec chemins de reprise, et une liste consolidée. Chaque étape est liée à des versions Nginx spécifiques (1.20.x–1.26.x stable/mainline), aux topologies de déploiement (bare metal systemd, Docker image officielle, Kubernetes NGINX Ingress Controller), et à des commandes observables avec sorties attendues, signaux d'échec et procédures de retour arrière.

1. Prérequis et hypothèses

Plage de versions et interprétation de nginx -V

Cet article couvre Nginx 1.20.x à 1.26.x (stable et mainline au 2024). Les différences majeures incluent :

  • 1.20+ : Support natif de grpc_pass, variables dans proxy_pass sans résolveur explicite pour les noms statiques.
  • 1.23+ : nginx -T affiche les fichiers inclus dans l'ordre de chargement ; modules dynamiques chargés via load_module au niveau main.
  • 1.25+ : Support expérimental QUIC/HTTP3 (nécessite compilation avec --with-http_v3_module et BoringSSL/QuicTLS ; absent des binaires officiels standards).
  • Plus vs OSS : La directive health_check dans un bloc match est exclusive à NGINX Plus. L'OSS utilise proxy_next_upstream combiné à des vérifications externes (ex: nginx_upstream_check_module tiers ou sondes Kubernetes).

Exécutez nginx -V 2>&1 | head -20 pour capturer les arguments de compilation. Sortie annotée attendue :

nginx version: nginx/1.24.0 (nginx.org)
built by gcc 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04)
built with OpenSSL 3.0.2 15 Mar 2022 (TLS 1.3 support)
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx \
--modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log \
--with-http_ssl_module --with-http_v2_module --with-http_realip_module \
--with-http_stub_status_module --with-http_gzip_static_module \
--with-threads --with-file-aio --with-cc-opt='-g -O2 -ffile-prefix-map=...' \
--with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now'

Signaux d'échec : nginx: command not found (binaire absent du PATH), OpenSSL < 1.1.1 (pas de TLS 1.3), modules attendus manquants (ex: --with-http_v2_module absent).

Matrice de topologie de déploiement

  • Topologie — Gestion processus — Chemin config typique — Commande rechargement — Particularités
  • Bare metal (systemd) — Master/worker via systemd — /etc/nginx/systemctl reload nginxCAP_NET_BIND_SERVICE requis pour ports 80/443 sans root ; LimitNOFILE dans unit file
  • Docker (image officielle nginx:1.24-alpine) — Master/worker dans conteneur (PID 1) — /etc/nginx/ (volume monté) — docker kill -s HUP <container> ou nginx -s reload via docker exec — Lecture seule possible sur /etc/nginx ; docker inspect pour vérifier mounts
  • Kubernetes (NGINX Ingress Controller v1.9.x → Nginx 1.25.x) — Déploiement Deployment/DaemonSet — ConfigMap nginx-configuration + TLS Secrets — kubectl rollout restart deployment/ingress-nginx-controller -n ingress-nginx — Configuration pilotée par ConfigMap/Annotations ; pas d'accès direct aux fichiers ; kubectl exec pour nginx -t

Outils requis

  • Bare metal : nginx, systemctl, openssl, curl, jq, ss (iproute2), audit2allow (SELinux), logrotate.
  • Docker : docker CLI, docker exec, docker logs.
  • Kubernetes : kubectl (version compatible cluster), kubectl exec, kubectl logs, kubectl describe.
  • Commun : Accès lecture /var/log/nginx/ (ou stdout conteneur), droits sudo pour écriture config/reload, ulimit -n ≥ 65535 pour workers.

Base de sécurité (baseline)

  • TLS : 1.2+ uniquement (ssl_protocols TLSv1.2 TLSv1.3;), suites fortes (ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:...), ssl_prefer_server_ciphers off (TLS 1.3).
  • En-têtes : server_tokens off;, add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;, add_header X-Frame-Options SAMEORIGIN;.
  • Rate limiting : Définir zones limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; au niveau http.
  • Droits fichiers : Certificats/clés lisibles par l'utilisateur worker (ex: www-data:www-data, mode 640 pour clés).

2. Inventaire version et environnement

Commandes de capture d'état (lecture seule)

Exécutez dans l'ordre, horodatez chaque sortie (date -u +%Y-%m-%dT%H:%M:%SZ).

# 1. Horodatage référence
date -u +%Y-%m-%dT%H:%M:%SZ

# 2. Version courte
nginx -v
# Attendu : nginx version: nginx/1.24.0
# Échec : nginx: command not found

# 3. Version détaillée (modules, OpenSSL, compilateur)
nginx -V 2>&1 | tee /tmp/nginx_V_$(date -u +%Y%m%dT%H%M%SZ).log

# 4. Dump configuration complète testée (syntaxe + includes résolus)
nginx -T 2>&1 | tee /tmp/nginx_T_$(date -u +%Y%m%dT%H%M%SZ).log
# Attendu : Sortie massive commençant par "nginx: the configuration file /etc/nginx/nginx.conf syntax is ok"
# Échec : "nginx: [emerg] ..." → syntaxe invalide, ne pas recharger

# 5. Statut service selon topologie
# Bare metal
systemctl status nginx --no-pager
# Attendu : "Active: active (running)" ; PID master visible
# Docker
docker ps --filter "name=nginx" --format "table {{.Names}}\t{{.Status}}\t{{.Image}}"
# Kubernetes
kubectl get pods -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx -o wide
kubectl describe pod -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx | head -40

Métriques système à capturer

# Utilisateur worker et limites
ps auxf | grep -E 'nginx: (master|worker)' | grep -v grep
# Attendu : 1 master (root), N workers (www-data/nginx) ; N == worker_processes (ou auto = nb CPU)

# Limites fichiers ouverts (appliquées au master)
cat /proc/$(pgrep -o nginx)/limits | grep "Max open files"
# Attendu : Soft/Hard ≥ 65535 ; sinon ajuster `worker_rlimit_nofile` et systemd `LimitNOFILE`

# Répertoires et permissions
ls -ld /etc/nginx /var/log/nginx /var/cache/nginx
# Attendu : /etc/nginx accessible en lecture par worker ; /var/log/nginx écriture par worker

# SELinux/AppArmor
getenforce 2>/dev/null || aa-status 2>/dev/null | head -5
# Si Enforcing : vérifier `ausearch -m avc -c nginx` pour dénis

Signaux d'échec critiques : nginx -T non-zéro (config cassée), master absent, workers < attendus, limites fichiers < 1024, dénis SELinux avc: denied sur name_bind ou read config.

3. Chemin de configuration sûr

Flux de travail standardisé (zéro temps d'arrêt)

Principe : Observer → Sauvegarder → Modifier (scope minimal) → Tester → Diff → Recharger gracieux → Vérifier → Documenter.

# 1. Horodatage pré-changement
TS=$(date -u +%Y%m%dT%H%M%SZ)
echo "CHANGE_START $TS" | tee -a /var/log/nginx/ops-changelog.log

# 2. Sauvegarde atomique arborescence complète
sudo cp -a /etc/nginx /etc/nginx.bak.$TS
# Alternative Docker : docker cp <container>:/etc/nginx ./nginx.bak.$TS
# Alternative K8s : kubectl get configmap nginx-configuration -n ingress-nginx -o yaml > nginx-cm.bak.$TS.yaml

# 3. Édition ciblée (exemple : timeout upstream API unique)
# Fichier : /etc/nginx/conf.d/api-upstream.conf (scope : un seul location block)
# AVANT
location /api/ {
    proxy_pass http://backend-api;
    proxy_set_header Host $host;
}
# APRÈS (diff -u)
--- /etc/nginx/conf.d/api-upstream.conf.bak.20240115T100000Z    2024-01-15 10:00:00
+++ /etc/nginx/conf.d/api-upstream.conf    2024-01-15 10:05:00
@@ -3,6 +3,7 @@
     proxy_pass http://backend-api;
     proxy_set_header Host $host;
+    proxy_read_timeout 60s;
+    proxy_send_timeout 60s;
 }

Points de vigilance par version

  • include ordering : Les fichiers dans conf.d/ sont inclus par ordre alphabétique (glob *.conf). Préfixez par 00-, 10- pour forcer l'ordre.
  • Modules dynamiques : load_module modules/ngx_http_js_module.so; doit précéder http {} dans nginx.conf (niveau main). Chemin relatif à --modules-path (nginx -V).
  • Certificats TLS : ssl_certificate /etc/nginx/ssl/app.example.com.pem; et ssl_certificate_key /etc/nginx/ssl/app.example.com.key; doivent être lisibles par l'utilisateur worker (chown www-data:www-data, chmod 640 clé).
  • Résolution DNS upstream : Si proxy_pass http://backend.internal; utilise un nom DNS, ajoutez resolver 127.0.0.11 valid=10s ipv6=off; (Docker) ou IP DNS interne + resolver_timeout 5s; dans le bloc location ou server.

Vérification post-édition et activation

# 1. Test syntaxe (ne change PAS l'état)
sudo nginx -t
# Attendu : "syntax is ok" + "test is successful"
# Échec : "nginx: [emerg] ..." → CORRIGER avant d'aller plus loin

# 2. Diff configuration active vs sauvegarde (valide que SEUL le changement prévu est présent)
sudo nginx -T 2>/dev/null | diff -u /etc/nginx.bak.$TS/nginx.conf - | head -50
# Attendu : Diff ne montre QUE l'ajout de proxy_read_timeout 60s;

# 3. Rechargement gracieux (zero-downtime)
# Bare metal
sudo systemctl reload nginx
# Docker
docker kill -s HUP <container_id>
# Kubernetes (ConfigMap changée)
kubectl rollout restart deployment/ingress-nginx-controller -n ingress-nginx --timeout=60s

# 4. Vérification fonctionnelle immédiate
curl -sS -o /dev/null -w "%{http_code} %{time_total}\n" -H "Host: app.example.com" http://127.0.0.1/api/health
# Attendu : "200 0.045" (code 2xx, latence < seuil)
# Vérification en-tête réponse (si ajouté)
curl -sI -H "Host: app.example.com" http://127.0.0.1/api/health | grep -i "x-op-check"

Retour arrière (rollback) validé

# Si vérification échoue (code 5xx, timeout, config non prise en compte)
echo "ROLLBACK_START $(date -u +%Y%m%dT%H%M%SZ)" | tee -a /var/log/nginx/ops-changelog.log

# 1. Restaurer arborescence
sudo mv /etc/nginx /etc/nginx.failed.$TS
sudo mv /etc/nginx.bak.$TS /etc/nginx

# 2. Valider syntaxe restaurée
sudo nginx -t || { echo "CRITICAL: Rollback config also broken"; exit 1; }

# 3. Recharger config restaurée
sudo systemctl reload nginx

# 4. Confirmer service sain
curl -sS -o /dev/null -w "%{http_code}\n" -H "Host: app.example.com" http://127.0.0.1/api/health
# Attendu : 200
echo "ROLLBACK_COMPLETE $(date -u +%Y%m%dT%H%M%SZ)" | tee -a /var/log/nginx/ops-changelog.log

4. Vérification et diagnostics

Contrôles de santé (Health Checks)

# 1. HTTP/HTTPS avec résolution forcée (bypass DNS, test direct IP)
# Remplacez 192.0.2.10 par l'IP réelle du serveur Nginx (TEST-NET-1)
curl -v --resolve app.example.com:443:192.0.2.10 https://app.example.com/healthz 2>&1 | head -30
# Attendu :
# * Connected to 192.0.2.10 port 443
# * TLSv1.3, cipher TLS_AES_256_GCM_SHA384
# > GET /healthz HTTP/2
# < HTTP/2 200
# < server: nginx
# < x-op-check: ok
# < content-length: 0

# 2. Test syntaxe + statut service
nginx -t && systemctl is-active nginx
# Attendu : "active" (exit 0)

# 3. Ports à l'écoute (master process)
ss -ltnp | grep -E ':(80|443)\s'
# Attendu : LISTEN 0 511 *:80 *:* users:(("nginx",pid=1234,fd=6)) ; pid = master PID

Métriques et observabilité

Module stub_status (OSS, basique) — Activer dans un server block protégé :

location /nginx_status {
    stub_status on;
    allow 192.0.2.0/24; # Monitoring network only
    deny all;
}
curl -s http://192.0.2.10/nginx_status
# Sortie attendue :
# Active connections: 43
# server accepts handled requests
#  12345 12345 56789
# Reading: 6 Writing: 7 Waiting: 30

Exporteur Prometheus (nginx-prometheus-exporter) — Scrape http://exporter:9113/metrics pour nginx_http_requests_total, nginx_upstream_latency_seconds.

Analyse logs accès (latence upstream) :

# Top 10 requêtes les plus lentes (dernières 1000 lignes)
awk '{print $NF, $0}' /var/log/nginx/access.log | tail -1000 | sort -nr | head -10
# Format log recommandé (log_format main_ext) inclut $request_time $upstream_response_time

Validation TLS complète

# Chaîne, expiration, OCSP, SNI
openssl s_client -connect app.example.com:443 -servername app.example.com -verify_return_error < /dev/null 2>&1 | tee /tmp/tls_check.log

# Extraire dates certificat feuille
openssl s_client -connect app.example.com:443 -servername app.example.com < /dev/null 2>/dev/null \
- openssl x509 -noout -dates -subject -issuer -ext subjectAltName

# Attendu :
# notBefore=Jan 15 00:00:00 2024 GMT
# notAfter=Apr 14 23:59:59 2024 GMT
# subject=CN = app.example.com
# issuer=CN = R3, O = Let's Encrypt
# X509v3 Subject Alternative Name: DNS:app.example.com, DNS:www.app.example.com

# Vérifier OCSP Stapling (réponse dans handshake)
openssl s_client -connect app.example.com:443 -servername app.example.com -status < /dev/null 2>/dev/null | grep -A 17 "OCSP Response"
# Attendu : "OCSP Response Status: successful (0x0)" + "Cert Status: good"

Santé workers et corrélation logs

# 1. Cohérence master/workers
ps auxf | grep -E 'nginx: (master|worker)' | grep -v grep
# Attendu : 1 master (root), N workers (www-data) ; N == worker_processes

# 2. Erreurs récentes (alert/emerg/crit)
grep -E '\[(alert|emerg|crit)\]' /var/log/nginx/error.log | tail -20
# Signal échec : "connect() failed (111: Connection refused) while connecting to upstream"

# 3. Codes 5xx dans accès (dernière heure)
awk '$9 ~ /^5[0-9]{2}$/ && $4 > "['$(date -d '1 hour ago' +'%d/%b/%Y:%H')'"' /var/log/nginx/access.log | wc -l
# Seuil alerte : > 1% du trafic ou > 10/min

Signaux d'échec diagnostiques :

  • Healthz non-2xx + upstream timed out dans error.log → Problème upstream/réseau.
  • alert/emerg au rechargement → Config invalide non détectée par nginx -t (rare, ex: limite RL partagée épuisée).
  • Worker crash : SIGSEGV dans error.log + coredump → Bug module tiers ou binaire corrompu.

5. Modes de défaillance et récupération

5.1 Erreur de syntaxe sur rechargement

  • Signal — Cause typique — Récupération
  • nginx -t échoue ([emerg]) — Point-virgule manquant, directive inconnue, include fichier manquant — Ne pas recharger. Corriger fichier → nginx -t OK → recharger.
  • nginx -t OK mais reload échoue (logs) — Limite système (ulimit), SELinux, port déjà utilisé — Voir sections spécifiques ci-dessous.

5.2 Certificat/clé TLS mismatch ou expiré

# Vérifier correspondance modulus (doit être identique)
openssl x509 -noout -modulus -in /etc/nginx/ssl/app.example.com.pem | openssl md5
openssl rsa -noout -modulus -in /etc/nginx/ssl/app.example.com.key | openssl md5
# Si différents → régénérer paire ou retrouver clé correspondante.

# Remplacement atomique (zéro downtime)
TS=$(date -u +%Y%m%dT%H%M%SZ)
sudo cp /etc/nginx/ssl/app.example.com.pem /etc/nginx/ssl/app.example.com.pem.bak.$TS
sudo cp /etc/nginx/ssl/app.example.com.key /etc/nginx/ssl/app.example.com.key.bak.$TS
# Déployer nouveaux fichiers (certbot, script, manuel) avec bons droits
sudo chown www-data:www-data /etc/nginx/ssl/app.example.com.{pem,key}
sudo chmod 640 /etc/nginx/ssl/app.example.com.key
sudo nginx -t && sudo systemctl reload nginx
# Reverifier
openssl s_client -connect app.example.com:443 -servername app.example.com < /dev/null 2>/dev/null | openssl x509 -noout -dates

5.3 Upstream indisponible (502/504)

# 1. Diagnostic connectivité
# Résolution DNS (si upstream par nom)
dig +short backend-api.internal
# Test TCP direct
nc -zv backend-api.internal 80
# Ou via resolver Nginx (dans location) : vérifier directive `resolver` valide

# 2. Vérifier health_checks (Plus) ou proxy_next_upstream (OSS)
# OSS : proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
# Plus : match block + health_check dans upstream

# 3. Atténuation immédiate : retirer upstream défaillant
# Créer /etc/nginx/conf.d/upstream-disable-bad.conf
upstream backend-api {
    # server backend-api.internal:80;  # COMMENTÉ
    server backup-backend.internal:80 backup; # Si backup défini
}
sudo nginx -t && sudo systemctl reload nginx
# 4. Rétablir dès retour normal (procédure inverse)

5.4 Épuisement ressources (OOM / trop fichiers ouverts)

  • Signal — Diagnostic — Action
  • alert "too many open files" dans error.log — ulimit -n master < worker_connections * workers — 1. sudo systemctl edit nginx → ajouter LimitNOFILE=65535 2. nginx.conf : worker_rlimit_nofile 65535; 3. Redémarrage requis (systemctl restart nginx) car worker_rlimit_nofile ne s'applique qu'au démarrage master.
  • OOM Killer (dmesg: Kill process nginx) — worker_processes trop élevé ou fuite mémoire module tiers — Réduire worker_processes à auto ou nb CPU ; auditer modules tiers ; ajouter swap si absent.

5.5 Échec mise à jour binaire (live upgrade)

Procédure standard Nginx (nécessite binaire remplacé sur disque, ex: /usr/sbin/nginx.new) :

# 1. Démarrer nouveau master (ancien garde workers)
kill -USR2 $(cat /var/run/nginx.pid)
# Attendu : nouveau master PID dans /var/run/nginx.pid.newbin ; anciens workers toujours actifs

# 2. Vérifier nouveau master sain
sleep 2
nginx -t  # Test avec NOUVEAU binaire (PATH mis à jour)
curl -sI http://127.0.0.1/healthz  # Via nouveau master (même port)

# 3. Arrêt gracieux anciens workers (finissent requêtes)
kill -WINCH $(cat /var/run/nginx.pid.oldbin)
# Attendre vidange (surveiller `ss -ltnp` + logs)

# 4. Arrêt ancien master
kill -QUIT $(cat /var/run/nginx.pid.oldbin)

# ROLLBACK si nouveau master défaillant (étape 2 échoue) :
kill -HUP $(cat /var/run/nginx.pid.oldbin)  # Réveille ancien master
kill -QUIT $(cat /var/run/nginx.pid)        # Tue nouveau master

5.6 Déni SELinux / AppArmor

# Identifier déni récent
sudo ausearch -m avc -c nginx -ts recent | head -30
# Ou AppArmor
sudo dmesg -T | grep -i nginx | grep -i denied

# Générer module local (SELinux)
sudo audit2allow -M nginx_local < /var/log/audit/audit.log
sudo semodule -i nginx_local.pp
# Vérifier
sudo systemctl reload nginx

6. Checklist d'opérations consolidée

  • Phase — Action — Commande — Signal attendu — Signal échec — Rollback / Récupération — Rayon d'impact — Fréquence
  • Inventory — Capture version + args compile — nginx -V 2>&1 \&#124; tee /tmp/inv_V.log — OpenSSL ≥ 1.1.1, modules requis présents — command not found, module manquant — Vérifier installation / PATH — Global (lecture) — Hebdo / Ad-hoc
  • Inventory — Dump config active testée — nginx -T 2>&1 \&#124; tee /tmp/inv_T.log — "syntax is ok", includes résolus — [emerg] syntaxe — Ne pas changer config ; auditer diff vs git — Global (lecture) — Quotidien / Ad-hoc
  • Inventory — Vérification limites workers — cat /proc/$(pgrep -o nginx)/limits \&#124; grep "open files" — Soft=Hard=65535+ — < 1024 — systemctl edit nginx + LimitNOFILE + restart — Global — Mensuel
  • Config — Sauvegarde pré-changement — sudo cp -a /etc/nginx /etc/nginx.bak.$(date -u +%Y%m%dT%H%M%SZ) — Copie complète, permissions préservées — cp: cannot stat — N/A (pré-requis) — Global (fs) — À chaque changement
  • Config — Test syntaxe — sudo nginx -t — "syntax is ok" + "test is successful" — [emerg] + fichier:ligne — Ne pas recharger ; corriger fichier — Global (validation) — À chaque changement
  • Config — Diff config vs backup — nginx -T \&#124; diff -u /etc/nginx.bak.$TS/nginx.conf - — Uniquement changements prévus — Lignes inattendues — mv /etc/nginx.bak.$TS /etc/nginx + reload — Global — À chaque changement
  • Config — Rechargement gracieux — sudo systemctl reload nginxsystemctl is-active nginx = active ; master PID inchangé — failed + logs [alert] — Rollback config (voir ligne suivante) — Global (workers recyclés) — À chaque changement
  • Config — Rollback config — sudo mv /etc/nginx /etc/nginx.failed.$TS && sudo mv /etc/nginx.bak.$TS /etc/nginx && sudo nginx -t && sudo systemctl reload nginx — Healthz 200, config active = backup — Rollback lui-même échoue — Redémarrer service (systemctl restart nginx) — Global — Urgence
  • Verify — Health check HTTPS (resolve) — curl -v --resolve app.example.com:443:192.0.2.10 https://app.example.com/healthz — HTTP/2 200, server: nginx, TLS 1.3 — Non-2xx, timeout, TLS error — Vérifier upstream + logs ; rollback config si récent — Unique vhost — Quotidien / Post-changement
  • Verify — Validation TLS certificat — openssl s_client -connect app.example.com:443 -servername app.example.com < /dev/null \&#124; openssl x509 -noout -datesnotAfter > 30 jours — Expiré ou < 14 jours — Renouveler cert (certbot) + déployer + reload — Unique vhost — Quotidien (auto) / Hebdo (manuel)
  • Verify — Analyse erreurs 5xx — awk '\$9 ~ /^5[0-9]{2}$/' /var/log/nginx/access.log \&#124; tail -100 — < 1% requêtes / 5min — Pic > 5% ou > 50/min — Corréler error.log upstream ; désactiver upstream défaillant — Upstream concerné — Continu (alerting)
  • Recovery — Upstream 502 mitigation — Créer conf upstream sans serveur défaillant → nginx -treload — 502 stop, trafic vers backups/sains — Toujours 502 — Vérifier firewall/SG/DNS upstream ; restaurer conf upstream — Upstream unique — Ad-hoc incident
  • Recovery — Certificat expiré urgence — Déployer nouveau cert/key (bons droits) → nginx -treloadopenssl verify — TLS handshake OK, notAfter valide — Mismatch modulus, droits fichiers — Restaurer ancien cert/key (backup) + reload — Unique vhost — Ad-hoc incident
  • Recovery — Limite fichiers atteinte — systemctl edit nginxLimitNOFILE=65535 + nginx.conf: worker_rlimit_nofile 65535;systemctl restart nginxcat /proc/<pid>/limits = 65535 — Restart échoue (config) — Revenir unit file + config précédent + restart — Global (restart = drop connections) — Rare (capacity planning)

Liens documentation officiels (placeholders) :

  • Core modules : http://nginx.org/en/docs/ngx_core_module.html
  • HTTP modules : http://nginx.org/en/docs/http/ngx_http_core_module.html
  • SSL/TLS : http://nginx.org/en/docs/http/ngx_http_ssl_module.html
  • Upstream : http://nginx.org/en/docs/http/ngx_http_upstream_module.html
  • Control signals : http://nginx.org/en/docs/control.html
  • Ingress Controller : https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/nginx-configuration/configmap.md

7. Scénario technique réaliste : Rotation certificat Let's Encrypt zéro-downtime

Contexte : app.example.com servi par Nginx 1.24 sur bare metal (Ubuntu 22.04, systemd). Certificat géré par certbot (standalone ou webroot). Renouvellement automatique via systemd timer mais déploiement manuel contrôlé.

Étapes opérationnelles

# 0. Prérequis : certbot installé, domaine validé, Nginx config existante avec ssl_certificate /etc/nginx/ssl/app.example.com.pem
TS=$(date -u +%Y%m%dT%H%M%SZ)
echo "CERT_ROTATE_START $TS app.example.com" | tee -a /var/log/nginx/ops-changelog.log

# 1. Renouvellement certbot (dry-run d'abord)
sudo certbot renew --dry-run --nginx
# Attendu : "The dry run was successful."

# 2. Renouvellement réel (produit nouveaux fichiers dans /etc/letsencrypt/live/app.example.com/)
sudo certbot renew --nginx --quiet
# Attendu : "Successfully renewed certificate."

# 3. Déploiement atomique vers emplacement Nginx (droits corrects)
sudo cp -L /etc/letsencrypt/live/app.example.com/fullchain.pem /etc/nginx/ssl/app.example.com.pem.new
sudo cp -L /etc/letsencrypt/live/app.example.com/privkey.pem /etc/nginx/ssl/app.example.com.key.new
sudo chown www-data:www-data /etc/nginx/ssl/app.example.com.{pem,key}.new
sudo chmod 644 /etc/nginx/ssl/app.example.com.pem.new
sudo chmod 640 /etc/nginx/ssl/app.example.com.key.new

# 4. Bascule atomique (mv est atomique sur même FS)
sudo mv /etc/nginx/ssl/app.example.com.pem.new /etc/nginx/ssl/app.example.com.pem
sudo mv /etc/nginx/ssl/app.example.com.key.new /etc/nginx/ssl/app.example.com.key

# 5. Validation syntaxe + diff
sudo nginx -t
# Attendu : OK
sudo nginx -T 2>/dev/null | diff -u /etc/nginx.bak.$TS/nginx.conf - | grep -E 'ssl_certificate|ssl_certificate_key'
# Attendu : Aucun diff (chemins identiques) ; seul contenu fichiers a changé

# 6. Rechargement gracieux
sudo systemctl reload nginx
# Attendu : Active (running), master PID stable, nouveaux workers

# 7. Vérification TLS post-reload (FORCER IP pour bypass cache DNS local)
curl -v --resolve app.example.com:443:192.0.2.10 https://app.example.com/healthz 2>&1 | grep -E "HTTP/|server:|TLSv"
# Attendu : HTTP/2 200, server: nginx, TLSv1.3

openssl s_client -connect app.example.com:443 -servername app.example.com < /dev/null 2>/dev/null \
- openssl x509 -noout -dates -subject

# Attendu : notAfter = date future (> 60 jours), subject = CN=app.example.com

# 8. Vérification logs (aucune erreur SSL)
grep -i ssl /var/log/nginx/error.log | tail -5
# Attendu : Aucune ligne récente (dernières 2 min)

echo "CERT_ROTATE_SUCCESS $TS" | tee -a /var/log/nginx/ops-changelog.log

Plan de retour arrière (si étape 7 ou 8 échoue)

echo "CERT_ROTATE_ROLLBACK $(date -u +%Y%m%dT%H%M%SZ)" | tee -a /var/log/nginx/ops-changelog.log

# 1. Restaurer anciens fichiers (certbot garde archives dans /etc/letsencrypt/archive/)
PREV=$(ls -1t /etc/letsencrypt/archive/app.example.com/fullchain*.pem | head -2 | tail -1)
PREV_KEY=$(ls -1t /etc/letsencrypt/archive/app.example.com/privkey*.pem | head -2 | tail -1)
sudo cp -L "$PREV" /etc/nginx/ssl/app.example.com.pem
sudo cp -L "$PREV_KEY" /etc/nginx/ssl/app.example.com.key
sudo chown www-data:www-data /etc/nginx/ssl/app.example.com.{pem,key}
sudo chmod 644 /etc/nginx/ssl/app.example.com.pem
sudo chmod 640 /etc/nginx/ssl/app.example.com.key

# 2. Valider + recharger
sudo nginx -t && sudo systemctl reload nginx

# 3. Reverifier
curl -sS -o /dev/null -w "%{http_code}\n" --resolve app.example.com:443:192.0.2.10 https://app.example.com/healthz
# Attendu : 200
echo "CERT_ROTATE_ROLLBACK_COMPLETE $(date -u +%Y%m%dT%H%M%SZ)" | tee -a /var/log/nginx/ops-changelog.log

Conclusion

Une checklist d'exploitation Nginx production n'a de valeur que si chaque recommandation est liée à une version spécifique, observable par des commandes standard, et réversible via une procédure testée. Copier une commande sans valider les prérequis (version, topologie, permissions, limites système) et sans comparer la sortie réelle au signal attendu n'est pas une procédure d'exploitation — c'est une source d'incidents.

Le flux de travail fiable suit une boucle fermée : Inventorier (état horodaté) → Préparer (sauvegarde + scope minimal) → Valider (syntaxe + diff) → Activer (rechargement gracieux) → Vérifier (healthz + TLS + logs) → Documenter (changelog + rollback testé). Chaque étape définit explicitement son signal de succès, son signal d'échec, et son chemin de récupération avant que l'opérateur n'exécute la commande suivante.

Prochaine action concrète : sélectionnez une vérification à faible risque (ex: nginx -T diff vs backup hebdomadaire, ou healthz --resolve sur un vhost non critique), enregistrez l'état actuel dans un terminal horodaté, exécutez la commande documentée, comparez la sortie au tableau des signaux attendus, et consignez le résultat. C'est ainsi que l'on transforme une checklist en mémoire opérationnelle d'équipe.

Recherches connexes

Score de qualité de l’article

Utilité pour le lecteur 97%
  • check_circle Guide prêt à lire
  • check_circle Exemples pratiques inclus
  • check_circle URL d’article optimisée pour le SEO