Vous avez développé une application Node.js (Express, Next.js, NestJS, Fastify, Strapi…) et vous voulez la mettre en prod proprement. L'approche classique et robuste : Node écoute en local sur un port haut, un reverse proxy (Apache ou Nginx) expose l'app en HTTPS sur le port 443, et pm2 garde le process vivant : redémarrage automatique en cas de crash, au reboot du serveur, et zero-downtime sur les déploiements.
Ce guide couvre le déploiement sur un VPS ou une offre Datacampus compatible Node.js, avec un utilisateur Linux dédié (jamais root pour l'app).
1. Installer Node.js via nvm
On évite les paquets nodejs d'Ubuntu/Debian qui sont souvent anciens. nvm (Node Version Manager) installe Node dans votre home, sans sudo, et permet de jongler entre plusieurs versions.
# En tant qu'utilisateur applicatif (pas root)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
# Recharger le shell
source ~/.bashrc
# Installer et activer Node 20 LTS
nvm install 20
nvm use 20
nvm alias default 20
# Vérifier
node -v # v20.x.x
npm -v
~/.nvm. Si vous avez plusieurs apps sous plusieurs utilisateurs Linux, chaque utilisateur aura sa propre installation Node. C'est voulu, et ça évite les conflits de version entre apps.
2. Installer et utiliser pm2
pm2 est un gestionnaire de process Node. Il relance l'app en cas de crash, gère les logs, permet le reload zero-downtime et peut lancer plusieurs instances en mode cluster.
# Installer pm2 globalement dans la version Node courante
npm install -g pm2
# Démarrer l'app
cd ~/apps/mon-app
pm2 start app.js --name mon-app
# Variantes utiles
pm2 start npm --name mon-app -- start # npm start
pm2 start "node dist/main.js" --name api # NestJS build
pm2 start "next start -p 3000" --name site # Next.js prod
# Gestion quotidienne
pm2 status # liste des apps
pm2 logs mon-app # logs live
pm2 restart mon-app # restart brutal
pm2 reload mon-app # zero-downtime (cluster mode)
pm2 stop mon-app
pm2 delete mon-app
Démarrage automatique au boot
Pour que pm2 relance vos apps après un reboot serveur :
# 1. Sauvegarder la liste d'apps en cours
pm2 save
# 2. Générer la commande systemd (à adapter à votre user)
pm2 startup
# pm2 affiche une ligne du genre :
# sudo env PATH=... pm2 startup systemd -u monuser --hp /home/monuser
# Copiez-la, vérifiez le user et le home, puis exécutez-la.
sudo pm2 startup sans précautionsudo pm2 startup directement, pm2 s'installe au boot sous root, avec les apps du root, pas les vôtres. Lancez pm2 startup sans sudo d'abord, copiez la commande qu'il vous suggère (elle contient -u votre_user --hp /home/votre_user), et exécutez-la telle quelle.
3. Reverse proxy Apache (hébergement Plesk)
Sur une offre mutualisée Plesk, l'app Node tourne en local sur 127.0.0.1:3000 et Apache relaie les requêtes HTTPS.
Dans Plesk, ouvrez le domaine puis Apache & nginx Settings. Dans le champ Directives Apache additionnelles pour HTTPS :
ProxyPreserveHost On
ProxyRequests Off
ProxyPass / http://127.0.0.1:3000/
ProxyPassReverse / http://127.0.0.1:3000/
# WebSocket (si votre app utilise socket.io, SSE, etc.)
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) ws://127.0.0.1:3000/$1 [P,L]
Les modules mod_proxy et mod_proxy_http doivent être actifs (ils le sont par défaut sur nos hébergements). Si Nginx est en front d'Apache dans Plesk, désactivez Smart static files processing pour que Nginx ne court-circuite pas le proxy.
4. Reverse proxy Nginx
Sur un VPS avec Nginx en direct, le bloc standard pour proxifier Node :
server {
listen 80;
listen [::]:80;
server_name monapp.exemple.fr;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
# Headers standards
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Timeouts (utile pour du SSE/long polling)
proxy_read_timeout 60s;
proxy_send_timeout 60s;
}
}
Testez la config puis rechargez :
sudo nginx -t && sudo systemctl reload nginx
5. HTTPS
Le reverse proxy gère le TLS : Node reste en HTTP sur 127.0.0.1, c'est voulu. Deux cas :
- Plesk : onglet Certificats SSL/TLS → Let's Encrypt, activez le certificat, Plesk le renouvelle automatiquement.
- VPS Nginx : utilisez
certbot—sudo certbot --nginx -d monapp.exemple.frajoute le bloc 443 et planifie le renouvellement.
Dans votre app Node, si vous lisez req.protocol ou req.ip, activez la confiance du proxy. Exemple Express :
app.set('trust proxy', 1); // fait confiance au 1er proxy en amont
6. Logs
pm2 centralise stdout et stderr de chaque app :
pm2 logs mon-app # live, les deux flux
pm2 logs mon-app --err # erreurs seulement
pm2 logs mon-app --lines 200 # historique
pm2 flush # vider les fichiers de log
Sans rotation, les logs grossissent indéfiniment. Installez le module officiel :
pm2 install pm2-logrotate
# Config recommandée
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 14
pm2 set pm2-logrotate:compress true
7. Monitoring
Un dashboard terminal en temps réel :
pm2 monit
CPU, RAM, logs, uptime, nombre de restarts. Pour du monitoring externe, pm2 s'interface avec Keymetrics (pm2.io), Datadog, New Relic ou tout exporter Prometheus tiers.
8. Déploiement sans coupure
Après un git pull + npm ci + build :
# Zero-downtime (cluster mode uniquement)
pm2 reload mon-app
# Brutal : tue puis relance
pm2 restart mon-app
Pour que reload soit réellement sans coupure, démarrez l'app en cluster mode — pm2 lance plusieurs workers, en remplace un à la fois :
pm2 start app.js --name mon-app -i max # un worker par coeur CPU
# ou
pm2 start app.js --name mon-app -i 2 # 2 workers
Attention : votre app doit être stateless côté mémoire (sessions en Redis, pas en app.locals).
9. Ecosystem file : la config propre
Pour plusieurs apps ou une config stable versionnée, créez ecosystem.config.js à la racine du projet :
module.exports = {
apps: [
{
name: 'api',
script: './dist/main.js',
instances: 2,
exec_mode: 'cluster',
max_memory_restart: '500M',
env: {
NODE_ENV: 'production',
PORT: 3000,
},
},
{
name: 'worker',
script: './dist/worker.js',
instances: 1,
env: {
NODE_ENV: 'production',
},
},
],
};
Puis :
pm2 start ecosystem.config.js
pm2 reload ecosystem.config.js # après un deploy
pm2 save
10. Pièges classiques
- Port déjà utilisé :
EADDRINUSE. Un ancien process traîne,pm2 delete alloulsof -i :3000pour identifier. - Firewall interne, le port applicatif (3000, 3001…) ne doit pas être ouvert sur l'extérieur. Bindez sur
127.0.0.1, pas sur0.0.0.0. NODE_ENV=productionoublié — Express sert les stack traces complètes en dev, les frameworks désactivent les optimisations. Définissez-le dansenvdu ecosystem file.- Memory leak qui coule le serveur :
pm2 start app.js --max-memory-restart 500Mredémarre proprement dès que l'app dépasse un seuil, en attendant que vous trouviez la fuite. - pm2 installé sous une autre version Node, si vous changez de Node via
nvm use, réinstallez pm2 et régénérez lestartup. - Permissions sur le home — le script systemd généré par
pm2 startupcontient le path du home ; si vous migrez l'app, régénérez-le.