Avancé ⏱ 20 min de mise en œuvre Mis à jour le 11 août 2026

Déployer une application Node.js en production sur un VPS

Lancer node app.js suffit pour tester, pas pour la production. Voici comment déployer une app Node.js proprement sur un VPS avec PM2 et un reverse proxy, jusqu'aux mises à jour sans interruption.

Lancer une application avec node app.js suffit pour un test, mais pas pour une production fiable. Le processus s’arrête après un crash, ne redémarre pas avec le serveur, exploite généralement un seul cœur et laisse les logs difficiles à superviser. Une API indisponible après une exception ou un redémarrage du VPS peut rapidement interrompre un service SaaS, un backend métier ou un outil d’IA.

Une mise en production sérieuse repose sur trois éléments : PM2 pour maintenir et superviser les processus Node.js, un reverse proxy pour gérer le trafic HTTP et HTTPS, et une configuration système limitant l’exposition du serveur. Voici comment construire cette architecture sur un VPS Linux.

Prérequis pour héberger Node.js sur un VPS

Il faut disposer d’un VPS Linux, par exemple sous Debian ou Ubuntu, avec un accès SSH (voir se connecter en SSH). Les commandes applicatives doivent être exécutées avec un utilisateur non-root, souvent nommé deploy. Le compte root reste réservé aux opérations système qui nécessitent sudo.

Node.js peut être installé avec NodeSource ou avec nvm. nvm est pratique pour choisir précisément la version utilisée par l’application :

nvm install --lts
nvm use --lts
nvm alias default lts/*
node --version
npm --version

En production, utilisez une version LTS compatible avec votre projet. Le fichier package.json peut aussi déclarer la version attendue :

{
  "engines": {
    "node": ">=22"
  }
}

Le serveur doit également disposer de Git, d’un reverse proxy comme Nginx et des outils nécessaires à la compilation des éventuelles dépendances natives.

Organisez les fichiers dans un répertoire appartenant à l’utilisateur de déploiement, par exemple /srv/myapp. Évitez d’exécuter l’application depuis /root ou de lancer PM2 avec sudo, car cela créerait une seconde instance PM2 associée au mauvais utilisateur.

Récupérer et préparer l’application

Connectez-vous avec le compte de déploiement, puis clonez le dépôt :

mkdir -p /srv/myapp
cd /srv/myapp
git clone https://example.com/organisation/myapp.git app
cd app

Installez les dépendances avec npm ci :

npm ci

Contrairement à npm install, cette commande respecte strictement le fichier package-lock.json. Elle produit ainsi une installation reproductible et échoue si le manifeste et le verrou ne correspondent pas.

Si le projet utilise TypeScript, NestJS, Next.js ou une autre étape de compilation, construisez ensuite les fichiers destinés à la production :

npm run build

Testez le point d’entrée réellement généré. Selon le projet, il peut s’agir de app.js, server.js ou dist/main.js.

Les secrets ne doivent jamais être ajoutés au dépôt Git. Placez le fichier d’environnement dans un emplacement distinct et restreignez ses permissions :

sudo mkdir -p /etc/myapp
sudo nano /etc/myapp/myapp.env
sudo chown deploy:deploy /etc/myapp/myapp.env
chmod 600 /etc/myapp/myapp.env

Exemple de contenu :

DATABASE_URL=postgresql://user:password@127.0.0.1:5432/myapp
SESSION_SECRET=une-valeur-longue-et-aleatoire
API_KEY=secret

L’application peut charger ce fichier avec dotenv en utilisant un chemin fourni par ENV_FILE, ou avec l’option --env-file des versions récentes de Node.js. Seules les valeurs non sensibles, comme NODE_ENV et PORT, peuvent raisonnablement apparaître dans la configuration PM2.

Lancer Node.js avec PM2

Installez PM2 pour l’utilisateur de déploiement :

npm install -g pm2
pm2 --version

Pour une application simple dont le point d’entrée est app.js, le lancement en mode cluster tient en une commande :

pm2 start app.js -i max --name myapp

L’option -i max démarre autant de processus que de cœurs disponibles. PM2 répartit les connexions entre eux et redémarre automatiquement un processus qui plante. Le mode cluster suppose toutefois que l’application ne conserve pas de session ou d’état critique uniquement en mémoire. Les sessions, files de tâches et verrous partagés doivent être stockés dans Redis, une base de données ou un service adapté.

Pour une configuration versionnée et reproductible, créez un fichier ecosystem.config.js dans le projet :

module.exports = {
  apps: [
    {
      name: "myapp",
      script: "dist/main.js",
      cwd: "/srv/myapp/app",
      instances: "max",
      exec_mode: "cluster",
      env_production: {
        NODE_ENV: "production",
        PORT: 3000,
        ENV_FILE: "/etc/myapp/myapp.env"
      }
    }
  ]
};

Si le projet utilise "type": "module" dans package.json, nommez plutôt cette configuration ecosystem.config.cjs.

Démarrez ensuite l’application :

cd /srv/myapp/app
pm2 start ecosystem.config.js --env production
pm2 status

Enregistrez la liste des processus et configurez leur restauration au démarrage du VPS :

pm2 save
pm2 startup

pm2 startup affiche une commande sudo propre au système et à l’utilisateur courant. Exécutez exactement cette commande, puis lancez de nouveau pm2 save. Après une modification de la version de Node.js avec nvm, régénérez le service de démarrage afin qu’il référence le bon binaire.

Placer l’application derrière un reverse proxy HTTPS

Le port Node.js ne doit pas être accessible directement depuis Internet. Configurez l’application pour écouter sur 127.0.0.1:3000, puis laissez Nginx recevoir les connexions publiques sur les ports 80 et 443.

Créez /etc/nginx/sites-available/myapp :

server {
    listen 80;
    listen [::]:80;
    server_name app.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;

        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;

        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_read_timeout 60s;
    }
}

Activez et vérifiez la configuration :

sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/myapp
sudo nginx -t
sudo systemctl reload nginx

Après avoir dirigé le DNS de app.example.com vers le VPS, ajoutez un certificat TLS avec Let’s Encrypt via Certbot :

sudo certbot --nginx -d app.example.com
sudo certbot renew --dry-run

Nginx termine la connexion HTTPS puis transmet la requête au processus Node.js local. Si l’application génère des URL absolues, des cookies sécurisés ou applique une limitation par adresse IP, activez aussi la confiance du proxy selon le framework utilisé, par exemple app.set("trust proxy", 1) avec Express.

Sécurité, pare-feu et supervision

Le pare-feu doit autoriser uniquement SSH, HTTP et HTTPS. Avec UFW :

sudo ufw allow OpenSSH
sudo ufw allow "Nginx Full"
sudo ufw enable
sudo ufw status

N’ouvrez pas le port 3000. Vérifiez également que l’application écoute bien sur l’interface locale :

ss -ltnp

Maintenez le système et les dépendances applicatives à jour. Examinez les alertes avant toute montée de version majeure :

sudo apt update
sudo apt upgrade
npm audit

PM2 centralise les sorties standard et les erreurs :

pm2 logs myapp
pm2 logs myapp --lines 200
pm2 monit
pm2 describe myapp

Ajoutez une rotation pour empêcher les journaux de remplir le disque :

pm2 install pm2-logrotate

Prévoyez aussi une route de santé, par exemple /health, une surveillance externe et des sauvegardes testées pour les bases de données et les fichiers persistants. PM2 redémarre un processus, mais ne remplace ni une sauvegarde ni une supervision indépendante.

Pour un projet hébergé en France, un VPS TalCloud pourra constituer une option lorsqu’il sera disponible. TalCloud annonce notamment des serveurs en France, un cadre RGPD, du stockage NVMe, une protection Anti-DDoS Netrix et un support humain. Aucun tarif ou dimensionnement VPS ne doit être anticipé avant la publication de l’offre.

Déployer les mises à jour sans interruption

Pour une nouvelle version, revenez dans le dépôt, récupérez le code et reconstruisez l’application :

cd /srv/myapp/app
git pull --ff-only
npm ci
npm run build
pm2 reload ecosystem.config.js --env production
pm2 save

pm2 reload remplace progressivement les processus du cluster. Les instances existantes continuent de répondre pendant que les nouvelles démarrent, ce qui permet un déploiement zero-downtime lorsque l’application est compatible avec ce fonctionnement.

Avant le rechargement, exécutez les tests et les migrations nécessaires. Les migrations doivent rester compatibles avec l’ancienne et la nouvelle version pendant la transition. Vérifiez enfin l’état, les logs et la route de santé :

pm2 status
pm2 logs myapp --lines 100
curl --fail https://app.example.com/health