NSSM : faire de n'importe quel programme un service Windows
Faire tourner une appli Node, un script Python ou un agent en continu sur Windows : démarrage au boot, relance après plantage, journaux propres.
J'ai un PC Windows qui reste allumé à la maison. Dessus tourne un petit portail Next.js qui regroupe mes applis perso : repas, budget, planning. Pendant des semaines, je le lançais à la main dans un terminal. Une mise à jour Windows la nuit, et le matin plus rien ne répondait. Le jour où j'ai voulu que ça tienne tout seul, j'ai installé NSSM.
NSSM, pour Non-Sucking Service Manager, est un petit exécutable qui transforme n'importe quel programme en service Windows. Vous lui donnez un node.exe, un python.exe ou un binaire Go, il s'occupe du reste : démarrage au boot avant même l'ouverture de session, redémarrage si le programme plante, redirection des sorties vers des fichiers de log. C'est un outil gratuit, dans le domaine public, écrit par Iain Patterson. Pas de dépôt GitHub officiel : tout se télécharge sur le site.
Pourquoi un programme ne devient pas un service tout seul
Un service Windows n'est pas un programme comme les autres. Il doit dialoguer avec le gestionnaire de services (le Service Control Manager) : signaler qu'il démarre, répondre quand on lui demande de s'arrêter, rendre compte de son état. Un serveur Node ou un script Python ne savent pas faire ça.
Vous pouvez essayer sc.exe create avec votre node.exe. Le service s'enregistre, puis échoue au démarrage avec l'erreur 1053 : le programme n'a pas répondu au gestionnaire à temps.
NSSM se place entre les deux. C'est lui que Windows lance comme service, et c'est lui qui lance votre programme comme un processus enfant. Il parle au gestionnaire de services à la place de votre appli, surveille le processus et le relance s'il meurt.
L'ancien outil srvany de Microsoft faisait déjà la première moitié du travail. Il lançait le programme, mais ne regardait plus ce qui lui arrivait ensuite. Si l'appli plantait, le service restait « démarré » alors que plus rien ne tournait. NSSM règle exactement ce problème.
Installer NSSM
Trois façons, du plus simple au plus manuel :
winget install --id NSSM.NSSM -echoco install nssmOu télécharger le zip sur nssm.cc et prendre win64\nssm.exe. La dernière version stable est la 2.24. Elle date de 2014, mais elle fait le travail. Sur Windows 10 et 11, je conseille la préversion 2.24-101, disponible sur la même page, qui corrige des soucis de démarrage sur les versions récentes du système.
Si vous passez par le gestionnaire de paquets Chocolatey, nssm est ajouté au PATH directement. Avec winget, l'exécutable atterrit dans un dossier au nom à rallonge. Je le copie donc à un endroit fixe, C:\ProgramData\nssm\nssm.exe, pour que mes scripts ne dépendent pas d'un chemin qui change à chaque mise à jour.
Toutes les commandes qui créent ou modifient un service demandent un PowerShell administrateur.
Créer un premier service
La commande de base prend trois éléments : le nom du service, le programme et ses arguments.
nssm install MonApi "C:\Program Files\nodejs\node.exe" "server.js"
nssm set MonApi AppDirectory "D:\apps\mon-api"
nssm start MonApiAppDirectory est le dossier de travail. Ne l'oubliez pas : sans lui, votre appli démarre dans C:\Windows\System32 et ne trouve ni son .env ni ses fichiers relatifs.
Vous préférez cliquer ? nssm install MonApi sans autre argument ouvre une petite fenêtre avec des onglets pour chaque réglage. Pratique pour découvrir les options, mais je finis toujours par tout écrire dans un script, pour pouvoir réinstaller la machine en une commande.
Pour le reste du cycle de vie :
nssm status MonApi
nssm restart MonApi
nssm stop MonApi
nssm edit MonApi
nssm remove MonApi confirmLe service apparaît aussi dans services.msc, et Get-Service MonApi fonctionne comme pour n'importe quel autre service.
Les réglages qui comptent vraiment
NSSM a des dizaines d'options. En pratique, j'en utilise une dizaine sur chaque service.
| Réglage | À quoi il sert | Valeur que j'utilise |
|---|---|---|
AppDirectory | Dossier de travail | Le dossier de l'appli |
DisplayName, Description | Nom lisible dans services.msc | Une phrase claire |
Start | Mode de démarrage | SERVICE_AUTO_START |
AppExit Default | Que faire quand le programme s'arrête | Restart |
AppRestartDelay | Pause avant de relancer, en ms | 5000 |
AppStdout, AppStderr | Fichiers de log | Un dossier journaux/ |
AppRotateFiles, AppRotateBytes | Rotation des logs | 1 et 1048576 (1 Mo) |
AppEnvironmentExtra | Variables d'environnement | NODE_ENV=production |
ObjectName | Compte qui exécute le service | Selon le cas, voir plus bas |
DependOnService | Attendre un autre service | Par exemple une base de données |
Quelques précisions sur ceux qui piègent.
Le redémarrage. Par défaut, NSSM relance le programme quand il s'arrête, quelle qu'en soit la raison. S'il plante en moins de 1,5 seconde (réglage AppThrottle), NSSM considère qu'il boucle et espace les tentatives. Ajouter AppRestartDelay évite de marteler une base de données qui n'est pas encore prête.
Les logs. Sans AppStdout, tout ce que votre appli écrit dans la console part dans le vide. Avec la rotation activée, NSSM renomme le fichier quand il dépasse la taille fixée. Pensez à purger les anciens fichiers de temps en temps : NSSM ne le fait pas pour vous.
Le démarrage. SERVICE_DELAYED_AUTO_START lance le service une à deux minutes après le boot. C'est utile pour un service qui a besoin du réseau ou d'un disque externe. Pour un serveur web local, le démarrage automatique classique suffit.
L'arrêt. Quand vous arrêtez le service, NSSM envoie d'abord un Ctrl+C au programme, puis un message de fermeture, et ne coupe le processus de force qu'en dernier recours. Votre appli a donc une chance de fermer proprement ses connexions. Il arrête aussi les processus enfants, ce qui évite les node.exe orphelins qui gardent le port occupé.
Exemple complet : un serveur Next.js qui tourne en permanence
Voici le script qui fait tourner mon portail. Il se lance une seule fois, dans un PowerShell administrateur, après un pnpm build.
$Service = "Portail"
$Nssm = "C:\ProgramData\nssm\nssm.exe"
$Dossier = "D:\apps\portail"
$Node = (Get-Command node).Source
$Next = Join-Path $Dossier "node_modules\next\dist\bin\next"
$Journaux = Join-Path $Dossier "service\journaux"
& $Nssm install $Service $Node "`"$Next`" start -H 0.0.0.0 -p 3000"
& $Nssm set $Service AppDirectory $Dossier
& $Nssm set $Service DisplayName "Portail perso"
& $Nssm set $Service Description "Applis de la maison sur le port 3000."
& $Nssm set $Service Start SERVICE_AUTO_START
& $Nssm set $Service AppExit Default Restart
& $Nssm set $Service AppRestartDelay 5000
New-Item -ItemType Directory -Force $Journaux | Out-Null
& $Nssm set $Service AppStdout (Join-Path $Journaux "portail.log")
& $Nssm set $Service AppStderr (Join-Path $Journaux "portail.log")
& $Nssm set $Service AppRotateFiles 1
& $Nssm set $Service AppRotateBytes 1048576
& $Nssm start $ServiceUn détail que j'ai appris à mes dépens : ne lancez pas npm start ou pnpm start. Sur Windows, npm et pnpm sont des fichiers .cmd. NSSM lance alors cmd.exe, qui lance Node. Le Ctrl+C de l'arrêt n'arrive pas toujours jusqu'au serveur, et le port reste parfois occupé au redémarrage suivant. Pointez directement vers node.exe et le script JavaScript du framework, comme ci-dessus avec next/dist/bin/next.
Pour une mise à jour du code, pas besoin de réinstaller le service :
sc.exe stop Portail
(Get-Service Portail).WaitForStatus("Stopped", "00:00:20")
pnpm build
sc.exe start PortailPar défaut, seul un administrateur peut arrêter ou démarrer un service. Mon installeur ajoute donc à mon compte le droit de piloter ce service précis (avec sc.exe sdset). Le script de mise à jour se lance ensuite sans élévation.
Le serveur écoute sur 0.0.0.0 pour être joignable depuis les téléphones du Wi-Fi. Une règle de pare-feu limitée au réseau local complète l'installation. Pour y accéder depuis l'extérieur sans ouvrir de port sur la box, un tunnel Cloudflare ou Tailscale est la bonne étape suivante.
Sous quel compte tourne le service
Par défaut, NSSM installe le service sous LocalSystem. C'est le compte le plus puissant de la machine. Pratique, puisque tout marche, mais une faille dans votre appli donne alors tous les droits sur le PC.
Trois options, selon le besoin :
| Compte | Droits | Quand le choisir |
|---|---|---|
LocalSystem | Tous, sur la machine | À éviter pour un serveur exposé |
NT AUTHORITY\LocalService | Minimaux | Un programme qui n'a besoin que de ses propres fichiers |
| Votre compte utilisateur | Les vôtres | Une appli qui lit vos fichiers, utilise votre Node, vos clés SSH |
Pour mon portail, j'ai choisi mon propre compte : l'appli lit des fichiers dans mon dossier, utilise le Node installé pour moi, et se comporte exactement comme quand je la lance à la main.
nssm set Portail ObjectName ".\mon-compte" "mot-de-passe-windows"Il faut le mot de passe du compte, pas le code PIN de Windows Hello. Et le script ne doit jamais contenir ce mot de passe en clair : je le demande avec Read-Host -AsSecureString au moment de l'installation.
Les autres usages : petits processus et agents
Un serveur web n'est que le cas le plus courant. Tout ce qui doit tourner en continu sur un PC Windows peut passer par NSSM.
Une API Python. Pointez vers le python.exe de l'environnement virtuel, pas celui du système :
nssm install MonApiPy "D:\apps\api\.venv\Scripts\python.exe" "-m uvicorn main:app --host 127.0.0.1 --port 8000"
nssm set MonApiPy AppDirectory "D:\apps\api"Un binaire Go ou Rust. Le cas idéal : un seul exécutable, aucune dépendance. Pour une API écrite en Go, NSSM est la façon la plus simple de la faire tourner sur un serveur Windows.
Un bot ou un agent. Un bot Discord, un script qui surveille un dossier, un agent qui garde une connexion ouverte. J'ai par exemple un deuxième service qui garde une session Claude Code joignable depuis mon téléphone, dès le démarrage du PC.
Un outil qui n'existe pas en service. Un serveur Minecraft, un reverse proxy comme Caddy, un cloudflared, Syncthing… Beaucoup proposent leur propre installation en service. Quand ce n'est pas le cas, NSSM comble le manque.
Un cas où NSSM ne marche pas : les programmes avec une interface graphique. Depuis Windows Vista, les services tournent dans une session isolée, la session 0, sans bureau. Votre programme démarre, mais personne ne voit sa fenêtre. Pour une appli graphique au démarrage, une tâche planifiée à l'ouverture de session reste la bonne réponse.
Dépanner un service qui refuse de démarrer
Quand ça ne démarre pas, je vérifie dans cet ordre :
- Le journal de NSSM. Il écrit ses propres messages dans l'Observateur d'événements, journal Application, source
nssm. On y voit la commande exacte lancée et le code de sortie. - Le fichier
AppStdout. L'erreur de votre appli s'y trouve : module manquant, port occupé, variable d'environnement absente. - Le PATH. Un service sous
LocalSystemne voit pas le PATH de votre utilisateur. Unnodetrouvé dans votre terminal peut être introuvable pour le service. Utilisez des chemins complets. - Les lecteurs réseau. Un lecteur
Z:monté dans votre session n'existe pas pour un service. Utilisez le chemin UNC (\\serveur\partage) et un compte qui a le droit d'y accéder. - Le port.
Get-NetTCPConnection -LocalPort 3000montre quel processus occupe déjà le port.
Pour tester sans attendre un redémarrage, nssm restart MonApi puis nssm status MonApi. Si l'état passe à SERVICE_PAUSED, NSSM a détecté que l'appli plantait en boucle et a ralenti les tentatives : la cause est dans les logs.
NSSM ou une autre solution ?
NSSM n'est pas la seule option. Voici comment je choisis :
| Outil | Points forts | Limites |
|---|---|---|
| NSSM | Une commande, aucune configuration à écrire, redémarrage et logs intégrés | Plus de nouvelle version stable depuis 2014 |
| WinSW | Configuration en XML versionnable, projet maintenu | Un fichier XML et un exécutable à copier par service |
| Tâche planifiée « au démarrage » | Intégrée à Windows | Pas de vraie surveillance, redémarrage limité |
| PM2 | Familier pour les développeurs Node, tableau de bord | Pas un service Windows natif, il faut un outil de plus |
| Docker Desktop | Isolation complète | Lourd, et Docker Desktop ne démarre qu'à l'ouverture de session |
Sur un serveur Linux, la question ne se pose pas : systemd fait tout ça nativement, comme dans le guide pour héberger une appli Node.js sur un VPS. Sur Windows, NSSM est ce qui s'en rapproche le plus avec le moins d'effort.
L'absence de nouvelle version peut faire peur. En pratique, le gestionnaire de services Windows n'a pas changé depuis des années, et NSSM n'a pas besoin de suivre une API qui bouge. Je l'utilise sur Windows 11 sans aucun souci.
NSSM ne fait qu'une chose, et ce qu'il fait remplace un terminal laissé ouvert et la peur de la mise à jour nocturne. Si un programme doit tourner en continu sur un PC Windows, écrivez dix lignes de script NSSM, versionnez-les avec le projet, et vous n'y penserez plus. Le jour où ce PC ne suffit plus, le même programme ira très bien dans un conteneur Docker sur un vrai serveur.