SSD Nodes Learn Hosting plans →
Guide Matt ConnorDi Matt Connor · Aggiornato 2026-08-27

Come eseguire un programma come servizio systemd su VPS

Crea un servizio systemd per avviare il programma al boot, riavviarlo dopo un crash e leggere i log con journalctl. Include timer e hardening.

Cos’è un servizio systemd e perché utilizzarlo

Un servizio systemd è un piccolo file di testo che indica al server come eseguire un programma: avviarlo al boot, riavviarlo se termina in modo anomalo e inviare il relativo output al log di sistema. Questo è il suo unico compito. Un programma avviato manualmente in una sessione SSH termina non appena si chiude la sessione o si riavvia il server. Un programma gestito da un servizio systemd continua a essere eseguito perché è il server stesso a gestirlo, non la shell.

systemd è il sistema init di Ubuntu, Debian, Fedora e della maggior parte dei moderni server Linux. È il primo processo ad avviarsi e supervisiona tutti gli altri. Non è sempre stato così e vale la pena leggere come systemd ha sostituito gli script init precedenti dopo aver compreso il funzionamento di un unit file. Quando si scrive un service file, si affida il programma a questo supervisore. Questa guida mostra l’unità minima funzionante, le tre sezioni presenti in ogni unità, come abilitarla e leggerne i log, come eseguirla secondo una pianificazione con un timer e come limitarne i privilegi al minimo necessario.

Il servizio minimo funzionante

Un file di servizio si trova in /etc/systemd/system/, termina con .service e richiede soltanto poche righe. Creane uno per un programma in /usr/local/bin/myapp:

sudo nano /etc/systemd/system/myapp.service
[Unit]
Description=My application

[Service]
ExecStart=/usr/local/bin/myapp

[Install]
WantedBy=multi-user.target

Questa è un'unità completa e funzionante. ExecStart è il comando da eseguire. WantedBy=multi-user.target indica di avviare il servizio quando il server raggiunge il normale funzionamento multiutente; è questo che ne consente l'avvio al boot. Tutto il resto è un perfezionamento.

Le tre sezioni e il loro scopo

Ogni file unit è suddiviso in sezioni racchiuse tra parentesi quadre. Un servizio ne usa tre.

[Unit] descrive il servizio e le relative dipendenze. Le due righe che userai più spesso sono:

[Unit]
Description=My application
After=network-online.target
Wants=network-online.target

Description è l'etichetta descrittiva visualizzata in systemctl status. After=network-online.target indica a systemd di non avviare il programma finché la rete non è disponibile. Questo è importante per i programmi che si associano a una porta o stabiliscono una connessione in uscita.

[Service] definisce l'esecuzione del programma. Qui vengono inserite la maggior parte delle impostazioni:

[Service]
ExecStart=/usr/local/bin/myapp --config /etc/myapp/config.toml
WorkingDirectory=/opt/myapp
User=myapp
Restart=on-failure
RestartSec=5
Environment=LOG_LEVEL=info

User=myapp esegue il programma con un account senza privilegi anziché con root. È la riga più importante per la sicurezza. Qui non è presente alcuna riga Type=, quindi systemd usa il valore predefinito simple e presume che il processo ExecStart rimanga in primo piano. Un programma che crea un processo figlio e passa in background richiede invece il valore Type= appropriato al modo in cui viene avviato, altrimenti l'unità risulterà attiva mentre il demone reale sarà già terminato. Restart=on-failure e RestartSec=5 hanno una sezione dedicata più avanti, perché sono il motivo principale per cui si crea un servizio.

[Install] definisce cosa accade quando abiliti il servizio:

[Install]
WantedBy=multi-user.target

WantedBy=multi-user.target collega il servizio alla procedura di avvio quando esegui systemctl enable. Senza una sezione [Install], un servizio può essere avviato manualmente, ma non verrà avviato automaticamente dopo un riavvio.

Attivarlo e monitorarlo

Dopo aver scritto o modificato un file unit, ricarica systemd in modo che legga la modifica, quindi abilita e avvia il servizio con un unico comando:

sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service

daemon-reload è il passaggio che spesso viene dimenticato: systemd memorizza nella cache i file unit, quindi una modifica non ha effetto finché non ricarichi la configurazione. enable --now abilita il servizio all'avvio e lo avvia immediatamente. Verifica lo stato:

sudo systemctl status myapp.service
* myapp.service - My application
     Loaded: loaded (/etc/systemd/system/myapp.service; enabled)
     Active: active (running) since Wed 2026-07-15 22:40:11 UTC; 3s ago
   Main PID: 4123 (myapp)

Active: active (running) e enabled sono i valori attesi. Per leggere l'output del programma, chiedi al journal di mostrare soltanto questa unit:

sudo journalctl -u myapp.service -f

-f segue le nuove righe quando arrivano, come tail -f. Tutto ciò che il programma scrive sullo standard output o sullo standard error finisce qui, senza dover configurare alcun sistema di logging.

Riavvio in caso di errore: il motivo per cui sei qui

Il principale vantaggio di un servizio è che systemd riavvia il programma quando termina in modo anomalo. Bastano due righe:

[Service]
Restart=on-failure
RestartSec=5

Restart=on-failure riavvia il programma quando termina con un codice diverso da zero o quando termina a causa di un segnale di crash come SIGKILL o SIGSEGV. Una terminazione regolare o un arresto causato da SIGTERM, SIGINT, SIGHUP o SIGPIPE non attiva il riavvio. RestartSec=5 attende cinque secondi tra un tentativo e l'altro, evitando che un programma che termina immediatamente entri in un ciclo molto rapido. Puoi verificarlo terminando il processo e osservando come systemd lo riavvia. Usa SIGKILL: il valore predefinito SIGTERM viene considerato un arresto regolare, quindi on-failure non riavvierebbe il servizio:

sudo systemctl kill -s SIGKILL myapp.service
sudo systemctl status myapp.service

Entro cinque secondi, lo stato mostra un nuovo Main PID e di nuovo active (running). Questa è l'intera funzionalità ed è il motivo per cui un servizio è preferibile a lasciare un programma in esecuzione in tmux o screen.

Eseguilo con un utente senza privilegi e applica misure di hardening

Un servizio eseguito come root può fare qualsiasi cosa sul server se il programma viene compromesso. Eseguilo con un utente dedicato e aggiungi alcune direttive di systemd per limitarne l’ambito operativo. Crea prima un account di sistema senza accesso alla shell e senza home:

sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp

Imposta quindi User=myapp e aggiungi le direttive di hardening a [Service]:

[Service]
User=myapp
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true

Ogni riga elimina una funzionalità che il programma non richiede. NoNewPrivileges=true impedisce al processo di acquisire nuovi privilegi, anche tramite un binario setuid. PrivateTmp=true gli assegna un /tmp privato, non visibile agli altri processi. ProtectSystem=strict rende l’intero filesystem di sola lettura, tranne per alcuni percorsi specificati con ReadWritePaths=. ProtectHome=true nasconde completamente /home al processo. È lo stesso principio del privilegio minimo applicato quando si protegge un servizio con un firewall: concedi solo ciò che serve. Se hai letto la guida su chiudere la falla del firewall IPv6 su un VPS, questa è la parte applicata sull’host dello stesso principio. Per un servizio esposto a Internet, abbina queste misure di hardening a Fail2ban davanti a SSH e a un firewall con criterio predefinito di negazione.

Invece di digitare tutto manualmente e ricordare in modo errato una direttiva, genera un’unità completa con hardening e copiala:

Toolsystemd service and timer generator

Timer: il cron moderno

Un timer systemd esegue un servizio secondo una pianificazione ed è il sostituto moderno di un job cron. Un timer è composto da due file: un .service che esegue l'attività e un .timer che definisce quando eseguirla. Supponiamo di voler eseguire un backup ogni giorno alle 3:00. Il servizio esegue l'attività una volta e termina:

# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh

Type=oneshot indica a systemd che il programma viene eseguito, termina e si arresta, invece di rimanere in esecuzione. Il timer lo pianifica:

# /etc/systemd/system/backup.timer
[Unit]
Description=Run the nightly backup

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true

[Install]
WantedBy=timers.target

OnCalendar=*-*-* 03:00:00 indica le 3:00 di ogni giorno. Testa qualsiasi espressione di calendario con systemd-analyze calendar "*-*-* 03:00:00", che verifica che l'espressione sia valida e stampa le prossime esecuzioni previste. Persistent=true esegue un job mancato non appena il server torna disponibile, se alle 3:00 era spento; cron non può farlo. Nota che un timer viene abilitato tramite timers.target, non tramite multi-user.target. Abilita il timer, non il servizio:

sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timers

list-timers mostra tutti i timer con la prossima e l'ultima esecuzione, così puoi vedere immediatamente quando verrà eseguito di nuovo il job. Il generatore precedente crea per te il .service e il .timer associati quando attivi la modalità timer. Rispetto a una riga cron, un timer offre log reali nel journal, le stesse direttive di hardening di qualsiasi servizio e il recupero delle esecuzioni mancate fornito da Persistent=true. Cron è ancora adatto per un job semplice; un timer è lo strumento migliore quando il job è importante.

FAQ

Qual è la differenza tra un servizio systemd e un cron job?

Un servizio mantiene in esecuzione un programma a lunga durata: viene avviato al boot, viene riavviato in caso di errore e scrive i log nel journal. Un cron job esegue un comando breve secondo una pianificazione e poi termina. Se serve la pianificazione, ma anche i log nel journal, l'hardening e il recupero delle esecuzioni mancate, usare un timer systemd, che associa una pianificazione .timer a un servizio oneshot e sostituisce cron per la maggior parte delle attività sui server.

Dove devo inserire il file del servizio systemd?

Inserire le unità personalizzate in /etc/systemd/system/, usando un nome che termini con .service. Questa directory è destinata alle unità aggiunte dall'amministratore e ha priorità sulle unità fornite dai pacchetti in /lib/systemd/system/. Dopo aver creato o modificato un file in questa directory, eseguire sudo systemctl daemon-reload affinché systemd rilevi la modifica.

Come posso riavviare un servizio se termina in modo anomalo?

Aggiungere Restart=on-failure e RestartSec=5 alla sezione [Service], quindi eseguire sudo systemctl daemon-reload e riavviare il servizio. systemd riavvia il programma quando termina con un codice diverso da zero o muore a causa di un segnale di crash, attendendo cinque secondi tra un tentativo e l'altro. Verificarlo con sudo systemctl kill -s SIGKILL myapp.service. SIGTERM, il segnale predefinito, indica un arresto regolare e non attiva on-failure. Monitorare systemctl status per verificare la comparsa di un nuovo PID entro pochi secondi.

Come posso eseguire un servizio systemd come utente non-root?

Creare un account di sistema con sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp, quindi aggiungere User=myapp alla sezione [Service]. Aggiungere NoNewPrivileges=true, PrivateTmp=true e ProtectSystem=strict, in modo che il processo disponga solo degli accessi necessari. Eseguire un servizio come utente senza privilegi è la modifica singola più importante per migliorarne la sicurezza.

Perché il mio servizio non è stato avviato?

Eseguire systemctl status myapp.service per il riepilogo e journalctl -u myapp.service per l'output completo. Le cause più comuni sono un percorso errato in ExecStart, un WorkingDirectory mancante, un errore di autorizzazione perché User= non può leggere un file oppure un sudo systemctl daemon-reload dimenticato dopo una modifica. Il journal mostra il messaggio di errore generato dal programma, che di solito identifica direttamente il problema.