Aller au contenu

Node.js : mise en production avec systemd

Faire tourner une application Node.js comme service système : démarrage automatique, redémarrage en cas d’échec, journaux centralisés. Les exemples utilisent des noms génériques (monapp, /srv/monapp, port 3000). Node.js doit être installé avec NodeSource ou le paquet Ubuntu (voir Installation).

  • L’application tourne sous un utilisateur dédié, sans droits d’administration.
  • Elle écoute sur 127.0.0.1 ; un reverse proxy (Caddy) gère HTTPS et l’accès depuis Internet.
  • systemd démarre, surveille et redémarre le processus.
  • Les logs vont sur la sortie standard et sont lus avec journalctl.
Fenêtre de terminal
sudo useradd --system --create-home --home-dir /srv/monapp --shell /usr/sbin/nologin monapp
sudo -u monapp mkdir -p /srv/monapp/app

Copier le projet dans /srv/monapp/app (Git, rsync, archive), puis installer les dépendances de production :

Fenêtre de terminal
sudo rsync -a --delete --exclude node_modules ./ /srv/monapp/app/
sudo chown -R monapp:monapp /srv/monapp/app
cd /srv/monapp/app
sudo -u monapp npm ci --omit=dev

Fichier lisible uniquement par root (systemd le lit au démarrage) :

Fenêtre de terminal
sudo tee /etc/monapp.env <<'CONF'
NODE_ENV=production
PORT=3000
CONF
sudo chmod 600 /etc/monapp.env

Les identifiants (base de données, clés d’API) se placent dans ce fichier, jamais dans le code ni dans Git.

Trouver le chemin de Node.js :

Fenêtre de terminal
command -v node # par exemple /usr/bin/node

Créer /etc/systemd/system/monapp.service :

Fenêtre de terminal
sudo tee /etc/systemd/system/monapp.service <<'CONF'
[Unit]
Description=Application Node.js monapp
After=network.target
[Service]
Type=simple
User=monapp
Group=monapp
WorkingDirectory=/srv/monapp/app
EnvironmentFile=/etc/monapp.env
ExecStart=/usr/bin/node src/serveur.js
Restart=on-failure
RestartSec=5
# Durcissement de base
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
[Install]
WantedBy=multi-user.target
CONF

Activer et démarrer :

Fenêtre de terminal
sudo systemctl daemon-reload
sudo systemctl enable --now monapp
systemctl status monapp
curl http://127.0.0.1:3000/sante
Directive Rôle
User / Group Utilisateur sans droits d’administration
EnvironmentFile Charge les variables d’environnement
ExecStart Commande de démarrage, chemin absolu de node
Restart=on-failure Redémarre si le processus s’arrête avec une erreur
NoNewPrivileges Interdit l’élévation de privilèges
PrivateTmp /tmp privé pour le service
ProtectSystem=full /usr, /boot et /etc en lecture seule pour le service
Fenêtre de terminal
journalctl -u monapp -f # en direct
journalctl -u monapp -n 100 --no-pager # 100 dernières lignes
journalctl -u monapp --since "1 hour ago"
journalctl -u monapp -p err # erreurs uniquement
app.exemple.com {
encode zstd gzip
reverse_proxy 127.0.0.1:3000
}

Voir Installer Caddy sur Ubuntu et exemples de Caddyfile (en-têtes de sécurité, limites, etc.).

systemd envoie SIGTERM pour arrêter le service. L’application doit terminer les requêtes en cours puis quitter (voir Premiers pas) :

process.on('SIGTERM', () => {
serveur.close(() => process.exit(0));
});
Fenêtre de terminal
cd /srv/monapp/app
sudo -u monapp git pull # ou nouvelle copie des fichiers
sudo -u monapp npm ci --omit=dev
sudo systemctl restart monapp
systemctl status monapp

Prévoir un retour arrière : conserver la version précédente (tag Git ou copie du dossier) pour pouvoir la restaurer rapidement.

Fenêtre de terminal
sudo systemctl edit monapp
[Service]
MemoryMax=512M
Environment=NODE_OPTIONS=--max-old-space-size=384

MemoryMax plafonne la mémoire du service ; --max-old-space-size (en Mo) limite le tas JavaScript de Node.js, en restant en dessous du plafond.

PM2 est un gestionnaire de processus propre à Node.js (redémarrage, mode cluster, journaux). Sur Ubuntu, systemd fait déjà l’essentiel et évite une dépendance supplémentaire : PM2 se justifie surtout pour le mode cluster ou la gestion de plusieurs applications avec une même interface.

Ubuntu 24.04 et 26.04 : différences à connaître

Section intitulée « Ubuntu 24.04 et 26.04 : différences à connaître »