SSD Nodes Learn
Guide Matt ConnorDi Matt Connor · Aggiornato 2026-07-24

Come creare un servizio systemd su VPS

Impara a configurare un file .service per avviare programmi all'avvio, gestire il restart automatico in caso di crash e consultare i log tramite journalctl.

Cos'è un servizio systemd e perché è necessario

Un servizio systemd è un piccolo file di testo che indica al server come eseguire un programma: avviarlo all'avvio, riavviarlo in caso di crash e inviare l'output al log di sistema. Questo è il suo unico compito. Un programma avviato manualmente in una sessione SSH termina non appena si effettua il logout o il server si riavvia. Un programma configurato come servizio systemd continua a essere eseguito, poiché il server lo gestisce direttamente invece della shell.

systemd è il sistema di init su Ubuntu, Debian, Fedora e sulla maggior parte dei server Linux moderni. È il primo processo ad avviarsi e supervisiona tutti gli altri. Quando si scrive un file di servizio, si affida il programma a questo supervisore. Questa guida mostra l'unità minima funzionante, le tre sezioni presenti in ogni unità, come attivarla e leggerne i log, come eseguirla tramite un timer e come limitare i privilegi per farla girare con il minimo accesso necessario.

Il servizio più semplice funzionante

Un file di servizio si trova in /etc/systemd/system/, ha estensione .service e richiede solo 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 la modalità multi-user normale; questo permette l'avvio automatico al boot. Tutto il resto è ottimizzazione.

Le tre sezioni e la loro funzione

Ogni unit file è diviso in sezioni delimitate da parentesi quadre. Un service ne utilizza tre.

[Unit] descrive il service e le sue relazioni. Le due righe più utilizzate sono:

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

Description è l'etichetta leggibile dall'utente visibile in systemctl status. After=network-online.target indica a systemd di non avviare il programma finché la rete non è attiva; questo è fondamentale per qualsiasi processo che utilizzi una porta o effettui connessioni in uscita.

[Service] definisce come viene eseguito il programma. Qui si trovano 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 non privilegiato invece di root; questa è la riga più importante per la sicurezza. Restart=on-failure e RestartSec=5 hanno una sezione dedicata sotto, poiché sono il motivo principale per cui si crea un service.

[Install] definisce cosa accade quando si abilita il service:

[Install]
WantedBy=multi-user.target

WantedBy=multi-user.target è ciò che collega il service all'avvio quando si esegue systemctl enable. Senza una sezione [Install], il service può essere avviato manualmente ma non si avvierà automaticamente dopo un reboot.

Attivalo e monitoralo

Dopo aver scritto o modificato un unit file, ricarica systemd per applicare le modifiche. Successivamente, 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 i file unit in cache, quindi una modifica non ha effetto finché non ricarichi la configurazione. enable --now abilita il servizio all'avvio e lo avvia immediatamente. Verifica l'operazione:

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)

I risultati corretti sono Active: active (running) e enabled. Per leggere l'output del programma, interroga il journal filtrando solo per questa unit:

sudo journalctl -u myapp.service -f

Il comando -f segue le nuove righe man mano che arrivano, in modo simile a tail -f. Tutto ciò che il programma scrive su standard output o standard error viene registrato qui, senza necessità di configurare il logging.

Restart su errore, il motivo per cui sei qui

Il vantaggio principale di un service è che systemd riavvia il programma quando termina. Due opzioni lo permettono:

[Service]
Restart=on-failure
RestartSec=5

Restart=on-failure riavvia il programma se esce con un codice diverso da zero o se crasha a causa di un segnale come SIGKILL o SIGSEGV. Un'uscita pulita, o un arresto tramite SIGTERM, SIGINT, SIGHUP o SIGPIPE, non attiva il riavvio. RestartSec=5 attende cinque secondi tra i tentativi, evitando che un programma che crasha istantaneamente entri in un loop infinito. Verifica il funzionamento terminando il processo e osservando systemd che lo riavvia. Usa SIGKILL: il default SIGTERM è considerato un arresto pulito, quindi on-failure non riavvierebbe il service:

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

Entro cinque secondi, lo status mostrerà un nuovo Main PID e active (running). Questa è la funzione principale, ed è il motivo per cui un service è preferibile rispetto a un programma eseguito in tmux o screen.

Eseguilo come utente non privilegiato e applica l'hardening

Un servizio che gira come root può compromettere l'intero server se il programma viene sfruttato. Eseguilo con un utente dedicato e utilizza le direttive di systemd per limitarne l'accesso. Per prima cosa, crea un account di sistema senza login e senza home directory:

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

Successivamente, imposta User=myapp e aggiungi le direttive di hardening in [Service]:

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

Ogni riga rimuove una funzionalità non necessaria al programma. NoNewPrivileges=true impedisce al processo di acquisire nuovi privilegi, anche tramite un binary setuid. PrivateTmp=true fornisce un /tmp privato che nessun altro processo può vedere. ProtectSystem=strict rende l'intero filesystem in sola lettura, fatta eccezione per i percorsi specificati con ReadWritePaths=. ProtectHome=true nasconde completamente /home. Questo approccio segue il principio del minimo privilegio, analogamente all'uso di un firewall: fornisci al servizio solo ciò di cui ha bisogno. Se hai letto la guida su chiudere il gap del firewall IPv6 su un VPS, questa è la parte relativa all'host dello stesso concetto. Per un servizio esposto su internet, combina questo hardening con Fail2ban davanti a SSH e un firewall con policy default-deny.

Invece di digitare manualmente ogni comando rischiando errori, genera un file unit completo e protetto e copialo:

Toolsystemd service and timer generator

Timers: il cron moderno

Un timer di systemd esegue un servizio secondo un programma prestabilito; è il sostituto moderno dei cron job. Un timer è composto da due file: un .service che esegue il lavoro e un .timer che ne definisce l'orario. Supponiamo di voler eseguire un backup ogni giorno alle 3:00. Il servizio esegue il compito 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 chiude, invece di rimanere in memoria. 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. È possibile testare qualsiasi espressione di calendario con systemd-analyze calendar "*-*-* 03:00:00", che conferma la corretta analisi sintattica e stampa le prossime esecuzioni. Persistent=true esegue un lavoro saltato non appena il server viene riavviato se era spento alle 3:00, funzionalità non disponibile in cron. Si noti che un timer viene abilitato tramite timers.target e non tramite multi-user.target. Abilitare il timer, non il servizio:

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

list-timers mostra ogni timer con la prossima e l'ultima esecuzione, permettendo di vedere immediatamente quando verrà eseguito il prossimo job. Il generatore sopra citato crea automaticamente la coppia .service e .timer quando si attiva la modalità timer. Rispetto a una riga cron, un timer fornisce log reali nel journal, le stesse direttive di hardening di qualsiasi servizio e la gestione dei job saltati tramite Persistent=true. Cron è ancora adatto per compiti semplici; un timer è lo strumento migliore per compiti critici.

FAQ

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

Un servizio mantiene attivo un programma in esecuzione continua: si avvia all'avvio del sistema, si riavvia in caso di errore e registra i log nel journal. Un cron job esegue un comando breve secondo un programma prestabilito e poi termina. Se è necessaria la pianificazione ma si desiderano anche i log del journal, l'hardening e il recupero delle esecuzioni mancate, utilizzare un systemd timer. Questo accoppia un programma di .timer con un servizio oneshot e sostituisce cron per la maggior parte dei compiti server.

Dove devo inserire il mio file di servizio systemd?

Inserire le proprie unità in /etc/systemd/system/, con un nome che termina in .service. Quella directory è destinata alle unità aggiunte dall'amministratore e ha la priorità sulle unità incluse nei pacchetti in /lib/systemd/system/. Dopo aver creato o modificato un file, eseguire sudo systemctl daemon-reload affinché systemd rilevi la modifica.

Come posso far riavviare un servizio se va in crash?

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 questo termina con un codice di uscita diverso da zero o quando muore a causa di un segnale di crash, attendendo cinque secondi tra i tentativi. Testare con sudo systemctl kill -s SIGKILL myapp.service — SIGTERM, il segnale predefinito, è considerato un arresto pulito e non attiva on-failure — e verificare che systemctl status mostri 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 affinché il processo operi con il minimo livello di accesso necessario. Eseguire il servizio come utente non privilegiato è la modifica più importante per aumentarne la sicurezza.

Perché il mio servizio non si è 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 permessi perché User= non può leggere un file, o un sudo systemctl daemon-reload dimenticato dopo una modifica. Il journal mostra il messaggio di errore del programma stesso, che solitamente indica direttamente il problema.