Eseguire un file JAR come servizio su Windows Server 2019: guida completa e troubleshooting

Vuoi eseguire un file JAR come servizio su Windows Server 2019 e impedirgli di “avviarsi e poi arrestarsi”? In questa guida pratica trovi diagnosi, cause tipiche e una procedura completa per far partire il tuo XXX.jar in modo affidabile al boot del server, con log, recupero da errori e configurazioni ottimali.

Indice

Scenario e sintomi

Un’applicazione Java impacchettata come XXX.jar deve rimanere attiva in background senza richiedere login. Il servizio è stato creato con sc create … (magari puntando a un wrapper) oppure tramite WinSW/NSSM, ma in Servizi compare lo stato “Avviato e poi arrestato”. Nel Visualizzatore eventi si trovano messaggi come:

System.ComponentModel.Win32Exception (0x80004005): The system cannot find the file specified
in winsw.WrapperService...

Questo pattern indica quasi sempre che il processo wrapper riesce a partire, ma fallisce nel lanciare java.exe o nel raggiungere il JAR/working directory. Di conseguenza Windows considera il servizio terminato.

Perché accade

  • Variabile d’ambiente non valida o invisibile: il servizio gira come LocalSystem (o come un account dedicato) e vede solo le variabili di sistema. Se JAVA_HOME indica una cartella inesistente (per esempio dopo un aggiornamento/disinstallazione del JDK/JRE), o se è definita solo a livello utente, java.exe non si trova.
  • Percorsi e virgolette: path con spazi (es. C:\Program Files\...) richiedono virgolette. In sc la sintassi è pignola: spazi dopo gli = e corretta citazione del valore di binPath. Nei file XML di WinSW/NSSM, <executable> e <arguments> vanno riportati con attenzione.
  • Working directory errata: l’app prova a leggere file relativi che non esistono dal CWD predefinito del servizio. Serve impostare una directory di lavoro.
  • Permessi e account: se l’app accede a share di rete (\\server\share), LocalSystem potrebbe non bastare; va usato un account di servizio con i diritti appropriati.
  • Architettura o versione: un JRE a 32 bit in Program Files (x86) su server a 64 bit, o un JAVA_HOME puntato a una versione rimossa, generano “file non trovato”.
  • Percorsi lunghi: se la lunghezza supera i limiti Win32 e non è abilitato il supporto ai percorsi estesi, il wrapper può non vedere il file.

Soluzione rapida che risolve nella maggior parte dei casi

Impostare JAVA_HOME e PATH a livello di sistema, quindi riavviare il servizio.

1. Impostare a livello di sistema:
   JAVAHOME = C:\Program Files\Java\jre1.8.0401
   PATH      = %JAVA_HOME%\bin;%PATH%

2. Riavviare (o eseguire “sc stop / start ”).
3. Impostare il tipo di avvio su “Automatico”.

Nota: sostituisci il percorso con la versione reale presente sul server. Il riavvio del sistema non è obbligatorio; è sufficiente riavviare il servizio (e talvolta il processo wrapper) per far leggere le nuove variabili.

Guida passo passo completa

Verifica dell’installazione Java

Esegui queste verifiche in una console con privilegi elevati:

where java
java -version

Se where non restituisce un percorso in C:\Program Files\Java\... (o nella directory del tuo JDK/JRE), significa che PATH non è configurato oppure JAVA_HOME è errato.

Impostazione di JAVA_HOME e PATH a livello di sistema

Metodo GUI:

  1. Apri sysdm.cpl → scheda AvanzateVariabili d’ambiente.
  2. Nella sezione Variabili di sistema, crea/modifica JAVAHOME con il percorso dell’installazione (es. C:\Program Files\Java\jre1.8.0401).
  3. Aggiungi %JAVA_HOME%\bin all’inizio della variabile di sistema Path.

Metodo da prompt (impatta l’intero sistema):

setx JAVAHOME "C:\Program Files\Java\jre1.8.0401" /M
setx PATH "%JAVA_HOME%\bin;%PATH%" /M

Metodo PowerShell:

[Environment]::SetEnvironmentVariable('JAVAHOME','C:\Program Files\Java\jre1.8.0401','Machine')
$cur = [Environment]::GetEnvironmentVariable('PATH','Machine')
[Environment]::SetEnvironmentVariable('PATH',"C:\Program Files\Java\jre1.8.0_401\bin;$cur",'Machine')

Per confermare:

[Environment]::GetEnvironmentVariable('JAVA_HOME','Machine')
[Environment]::GetEnvironmentVariable('PATH','Machine') | Out-String

Creazione del servizio con WinSW

WinSW è un wrapper leggero che espone le API dei servizi Windows e lancia il processo Java. Flusso tipico:

  1. Posiziona l’eseguibile WinSW in C:\svc\myapp-service.exe (rinominando l’eseguibile in base all’ID del servizio).
  2. Nella stessa cartella, crea myapp-service.xml con il seguente contenuto base:
<service>
  <id>myapp-service</id>
  <name>My Java Service</name>
  <description>Esecuzione dell'applicazione XXX.jar come servizio.</description>

%JAVA_HOME%\bin\java.exe
-Xms512m -Xmx512m -Dfile.encoding=UTF-8 -jar "C:\app\XXX.jar"
C:\app




C:\app\logs

10485760 
8



20 sec

Installa e configura:

C:\svc\myapp-service.exe install
sc config myapp-service start= auto
sc start myapp-service

Vantaggi: variabili d’ambiente dichiarabili nel file, log rolling integrato, recupero da errori, working directory esplicita.

Creazione del servizio con NSSM

NSSM (Non-Sucking Service Manager) è un altro wrapper robusto. Procedura CLI tipica:

nssm install MyJavaService "C:\Program Files\Java\jre1.8.0_401\bin\java.exe" ^
  "-Xms512m -Xmx512m -jar C:\app\XXX.jar"
nssm set MyJavaService AppDirectory "C:\app"
nssm set MyJavaService AppStdout "C:\app\logs\stdout.log"
nssm set MyJavaService AppStderr "C:\app\logs\stderr.log"
nssm set MyJavaService Start SERVICEAUTOSTART
nssm start MyJavaService

NSSM semplifica l’impostazione della directory di lavoro e dei log standard senza file XML.

Registrazione con sc puntando al wrapper

Se preferisci usare sc direttamente per la registrazione, punta il binPath al wrapper (WinSW/NSSM), non a java.exe:

sc create myapp-service binPath= "C:\svc\myapp-service.exe" DisplayName= "My Java Service" start= auto
sc description myapp-service "Esegue XXX.jar con Java al boot del server"
sc start myapp-service

Attenzione alla sintassi: in sc serve lo spazio dopo =, e i percorsi con spazi vanno sempre tra virgolette.

Configurazione del recupero automatico

Per riavviare il servizio su crash:

sc failure myapp-service reset= 86400 actions= restart/5000/restart/5000/restart/5000

In alternativa, in services.msc → proprietà del servizio → scheda Recupero, imposta Riavvia il servizio ai primi eventi di errore.

Account del servizio, permessi e accesso a rete

  • Per accesso a share SMB, database o risorse di dominio, esegui con un account di servizio dedicato (locale o di dominio) con permessi minimi necessari.
  • Configura dal wrapper o con sc config: sc config myapp-service obj= "DOMINIO\svc-java" password= "PasswordSicura"
  • Valuta “Load User Profile” (NSSM) se l’app salva dati in profilo utente.

Diagnostica approfondita

Quando il servizio “parte e si ferma”:

  • Controlla i log del wrapper nella cartella del servizio (winSW genera file *.log in logpath o a fianco dell’eseguibile).
  • Event ViewerRegistri di WindowsApplicazione e Sistema. Cerca origine WinSW o Service Control Manager.
  • Verifica i percorsi: Test-Path "C:\Program Files\Java\jre1.8.0_401\bin\java.exe" Test-Path "C:\app\XXX.jar" icacls "C:\app" /T
  • Working directory: se l’app usa file relativi, assicurati che <workingdirectory> (WinSW) o AppDirectory (NSSM) sia impostata su C:\app.
  • Percorsi lunghi: se sospetti limiti, abilita i long paths tramite Criteri di gruppo o con il valore LongPathsEnabled=1 in HKLM\SYSTEM\CurrentControlSet\Control\FileSystem.
  • Architettura: preferisci JRE/JDK a 64 bit su OS a 64 bit e verifica che il percorso non punti a Program Files (x86) se non necessario.

Alternative utili

ObiettivoStrumento consigliatoPassaggi chiave
Creare/gestire il servizio in modo più sempliceWinSW o NSSMScaricare l’eseguibile → definire myapp.xml: <executable>%JAVA_HOME%\bin\java.exe</executable>, <arguments>-jar C:\app\XXX.jar</arguments>winsw install → avvio Automatico.
Avvio senza servizioTask SchedulerNuova attività → trigger At system startup → comando java -jar C:\app\XXX.jarRun whether user is logged on or not.

Buone pratiche da adottare

  • Log applicativi: indirizza stdout/stderr su file dedicati. Con WinSW: <logpath>C:\app\logs</logpath> <log mode="roll-by-size">...</log> Con NSSM usa AppStdout e AppStderr.
  • Verifica post-aggiornamento Java: dopo update o disinstallazione, ricontrolla JAVA_HOME, PATH e i percorsi in WinSW/NSSM.
  • Recupero da errori: in Proprietà del servizio → Recupero imposta Riavvia il servizio per garantire ripartenza in caso di crash.
  • Impostazioni JVM: definisci -Xms, -Xmx, -Dfile.encoding=UTF-8, -Duser.timezone=Europe/Rome se utile. Evita valori di heap eccessivi che potrebbero impedire l’avvio.
  • Dipendenze: se la tua app richiede un database o un servizio di rete, imposta le dipendenze per evitare partenze premature: sc config myapp-service depend= Tcpip
  • Sicurezza: assegna permessi NTFS minimi all’account del servizio sulla cartella C:\app e sui log; evita di eseguire come amministratore se non necessario.
  • Backup dei file di configurazione: includi myapp-service.xml o l’export della configurazione NSSM nel tuo sistema di versionamento.

Checklist rapida prima della messa in produzione

  • JAVA_HOME e PATH definiti a livello Machine.
  • WinSW/NSSM configurato con <executable> che punta a java.exe e <arguments> con -jar e il percorso assoluto del JAR.
  • Working directory impostata su C:\app o cartella equivalente.
  • Log in output su file con rotazione attiva.
  • Avvio automatico e recupero configurati.
  • Account servizio corretto e permessi minimi necessari.
  • Test di avvio, arresto e riavvio con sc start/stop e verifica dei log.

Domande frequenti

Posso creare un servizio puntando direttamente a java.exe -jar con sc create?
È sconsigliato: un processo generico non implementa le API dei servizi Windows e potrebbe non rispondere correttamente ai segnali di stop. Usa un wrapper (WinSW, NSSM, Procrun) o un Java Service Wrapper dedicato.

Devo riavviare il server dopo aver cambiato JAVA_HOME?
Non necessariamente: in genere basta riavviare il servizio. Se il wrapper rimane residente con vecchie variabili, riavvia anche il wrapper o il sistema.

Come faccio a verificare le variabili viste dal servizio?
Imposta temporaneamente, nel wrapper, la stampa delle variabili all’avvio o scrivi uno script che stampi %JAVA_HOME%/%PATH% e lanciarlo come executable per un test. Con WinSW puoi definire <env> nel file XML per bypassare eventuali incongruenze di sistema.

La mia app usa file relativi: perché non li trova?
Perché la directory di lavoro del servizio differisce dalla tua shell interattiva. Imposta <workingdirectory> (WinSW) o AppDirectory (NSSM) per allinearla.

Template pronti all’uso

File XML WinSW minimale

<service>
  <id>myapp-service</id>
  <name>My Java Service</name>
  <executable>%JAVA_HOME%\bin\java.exe</executable>
  <arguments>-jar "C:\app\XXX.jar"</arguments>
  <workingdirectory>C:\app</workingdirectory>
  <logpath>C:\app\logs</logpath>
  <onfailure action="restart" delay="5 sec"/>
</service>

Script di configurazione rapida con PowerShell

# Percorsi
$Jre = "C:\Program Files\Java\jre1.8.0_401"
$App = "C:\app\XXX.jar"
$SvcDir = "C:\svc"
$SvcId = "myapp-service"

Variabili di sistema

[Environment]: :SetEnvironmentVariable\('JAVA_HOME',$Jre,'Machine'\)

$cur = [Environment]::GetEnvironmentVariable('PATH','Machine')
if ($cur -notlike "$Jre\bin") {
[Environment]::SetEnvironmentVariable('PATH',"$Jre\bin;$cur",'Machine')
}

File XML WinSW

$xml = @"

$SvcId
My Java Service
%JAVA_HOME%\bin\java.exe
-Xms512m -Xmx512m -jar "$App"
$(Split-Path $App)
$(Split-Path $App)\logs


"@
$xml | Out-File -FilePath "$SvcDir$SvcId.xml" -Encoding UTF8

Registrazione e avvio

& "$SvcDir$SvcId.exe" install
sc.exe config $SvcId start= auto | Out-Null
sc.exe start  $SvcId | Out-Null
Write-Host "Servizio $SvcId installato e avviato."

Comandi di amministrazione utili

# Stato e configurazione
sc query myapp-service
sc qc myapp-service

Avvio/stop/riavvio

sc stop myapp-service
sc start myapp-service

Recupero su errori

sc failure myapp-service reset= 86400 actions= restart/5000/restart/5000/restart/5000

Dipendenze

sc config myapp-service depend= Tcpip

Esecuzione con account di servizio

sc config myapp-service obj= "DOMINIO\svc-java" password= "PasswordSicura"

Casi reali e soluzioni

Errore “The system cannot find the file specified” in WinSW: quasi sempre JAVAHOME o il percorso del JAR non esistono nel contesto del servizio. Imposta <env name="JAVAHOME" ... /> nel file XML e usa percorsi assoluti per arguments. Verifica con Test-Path.

Servizio che si ferma subito senza errori nei log: il processo Java esce per eccezione non catturata o per configurazione mancante. Abilita stdout/stderr su file, aumenta il livello di log dell’app, verifica credenziali di database/porte bindate.

Configurazione OK ma l’app non vede i certificati/keystore: specifica il keystore con parametri JVM nel wrapper:

-Djavax.net.ssl.keyStore="C:\app\cert\keystore.jks" -Djavax.net.ssl.keyStorePassword=

e assicurati che l’account abbia permessi di lettura.

Conclusioni

La chiave per eseguire un JAR come servizio su Windows Server 2019 è separare le responsabilità: il wrapper parla con il Service Control Manager, mentre Java esegue l’app. Con variabili d’ambiente corrette, percorsi assoluti e una directory di lavoro definita, il servizio risulta affidabile, si avvia al boot e si riprende automaticamente in caso di guasto. Le procedure e i template sopra ti permettono di passare da “avviato e poi arrestato” a una esecuzione stabile e osservabile in produzione.


Riepilogo delle azioni essenziali

  • Definisci JAVA_HOME e aggiorna PATH a livello di sistema.
  • Usa WinSW o NSSM per creare un vero servizio Windows per il tuo JAR.
  • Imposta working directory, log a file e recovery automatico.
  • Se necessario, esegui con un account di servizio con i permessi minimi.
  • Verifica con where java, java -version e i log del wrapper.

Seguendo questi passaggi, il tuo XXX.jar si avvierà regolarmente anche senza login utente.

Indice