Un servizio systemd che non parte è quasi mai un mistero. Lancia systemctl status <unit>, poi leggi le ultime 50 righe di journal per quell’unit con journalctl -u <unit> -n 50 --no-pager — tra i due, una di cinque cause di solito viene detta chiaramente: un percorso sbagliato, un binario mancante, permessi errati, un diniego SELinux/AppArmor o un errore di sintassi nell’unit file. Il motivo del fallimento è nel log; le correzioni qui sotto sono solo pattern matching contro di esso.
Come vedo perché un servizio systemd è fallito?
Prima lo status, poi il journal:
systemctl status myapp.service
journalctl -u myapp.service -n 50 --no-pager status ti dà lo stato (inactive (dead), failed (exit-code), activating (auto-restart)) e le ultime righe di log. Il journal ti dà la storia completa: stdout, stderr e le lamentele di systemd sull’unit stessa.
Se l’unit è già fallita in precedenza e vuoi la registrazione di quella esecuzione, aggiungi -b per il boot corrente o --since today:
journalctl -u myapp.service -b --no-pager Per l’arsenale completo — boot, priorità, output seguito in diretta — vedi il journalctl cheat sheet. È lo stesso riflesso muscolare.
Un’altra coppia che vale conoscere:
systemctl list-units --failed
systemctl reset-failed myapp.service La prima elenca ogni unit rossa sulla macchina. La seconda pulisce lo stato di fallimento dopo che l’hai corretto — cosmetica, ma evita che le pagine di status diano falsi allarmi.
Quali sono le cause più comuni?
Dopo che il log ha detto il sintomo, la causa è quasi sempre una di queste cinque:
| Il log dice | Causa probabile | Correzione |
|---|---|---|
status=203/EXEC | Percorso ExecStart= sbagliato o interprete mancante | Percorso assoluto, chmod +x, controlla lo shebang |
status=203/EXEC su uno script | Lo script ha fine riga CRLF o shebang sbagliato | dos2unix script.sh, correggi la prima riga |
status=1/FAILURE, nessun log dell’app | Directory di lavoro o variabile d’ambiente mancante | Imposta WorkingDirectory=, aggiungi Environment= |
Permission denied | L’utente non può leggere i file o fare bind sulla porta | Correggi la proprietà; le porte sotto 1024 vogliono AmbientCapabilities=CAP_NET_BIND_SERVICE o root |
Unit is masked | Qualcuno ha lanciato systemctl mask | systemctl unmask myapp.service |
| La modifica all’unit file «non fa niente» | Demone non ricaricato | systemctl daemon-reload |
La famiglia 203/EXEC merita una menzione speciale perché è quella che si mangia i pomeriggi. Systemd non usa la tua shell per lanciare ExecStart=. Questo significa:
# Sbagliato — niente shell, ~ non si espande mai, nessuna ricerca nel PATH
ExecStart=~/app/run.sh
# Giusto
ExecStart=/opt/app/run.sh E lo script stesso deve essere eseguibile e iniziare con un vero shebang (#!/bin/bash o #!/usr/bin/env bash). Uno script che gira bene dal tuo terminale ma fallisce con 203 sotto systemd è quasi sempre uno di questi casi: non eseguibile, fine riga CRLF, shebang che punta nel vuoto, o percorso relativo.
Perché parte a mano ma non al boot?
Il classico bug di ordinamento. Se il log mostra fallimenti subito dopo il boot ma l’unit parte bene quando la avvii a mano con systemctl start, il tuo servizio sta perdendo una corsa — cerca la rete, un disco montato o un database prima che quella cosa esista.
La correzione è dichiarare le dipendenze invece di sperare:
[Unit]
After=network-online.target postgresql.service
Wants=network-online.target
[Service]
ExecStartPre=/usr/bin/test -f /opt/app/config.toml After= ordina l’avvio; Wants= fa sì che systemd alzi davvero la dipendenza. network-online.target funziona solo se il servizio di attesa rete è abilitato sulla tua distro, quindi controlla systemctl is-enabled NetworkManager-wait-online.service (o l’equivalente systemd-networkd). La guardia ExecStartPre= è un modo economico e onesto di fallire rumorosamente con un messaggio leggibile invece di uno stack trace.
Una seconda variante: il servizio parte al boot ma muore subito. Cerca le cose che il tuo ambiente interattivo aveva e il boot no — differenze di PATH, un virtualenv, una HOME. Imposta esplicitamente ciò che serve:
[Service]
Environment="PATH=/opt/app/venv/bin:/usr/bin"
User=appuser
WorkingDirectory=/opt/app Perché il mio servizio non logga nel journal?
Se journalctl -u non mostra nulla, controlla tre cose in ordine:
StandardOutput=eStandardError=nell’unit — devono esserejournal(il default) ojournal+console. Qualcuno potrebbe averli messi anull.- L’applicazione scrive su un file invece che su stdout. Systemd cattura solo stdout/stderr; chi scrive su file di log bypassa completamente il journal. O punti l’applicazione a stdout o leggi il file.
- I limiti di archiviazione hanno scartato le righe vecchie:
journalctl --disk-usage, eSystemMaxUse=in/etc/systemd/journald.confse il journal sta soffocando.
Per fare debug dell’avvio stesso, niente batte l’infilare una shell nel contesto del boot:
[Service]
ExecStart=/bin/bash -c 'exec /opt/app/bin/server 2>&1' Oppure, per i misteri veri, esegui il comando esatto di ExecStart= come utente dell’unit in una shell — la maggior parte delle differenze d’ambiente emerge nei primi dieci secondi.
Come lo faccio ripartire da solo dopo un crash?
La policy di default è Restart=no: un servizio crashato resta morto, e te lo scopri da un utente. Correggilo per servizio:
[Service]
Restart=on-failure
RestartSec=5 | Impostazione | Riavvia quando |
|---|---|
no (default) | Mai |
on-failure | Uscita non zero, segnale, timeout |
always | Qualsiasi uscita, anche pulita |
on-watchdog | Solo timeout del watchdog |
Abbina Restart=on-failure a una guardia sul tasso di avvio, così un loop di crash non martella la macchina: StartLimitIntervalSec= e StartLimitBurst= nella sezione [Unit]. Cinque fallimenti in sessanta secondi dovrebbero chiamare un essere umano, non far girare una CPU.
Se lo stai montando come job pianificato invece che come demone, valuta prima cron vs timer di systemd — i timer ti danno logging nel journal e ordinamento delle dipendenze gratis, che è esattamente ciò che questo articolo continua a cercare.
La checklist da 60 secondi
systemctl status <unit>— leggi lo stato e le ultime righe.journalctl -u <unit> -n 50 --no-pager— trova l’errore vero.203/EXEC? Correggi percorso, shebang, permessi.Permission denied? Correggi utente e proprietà dei file.- Fallisce solo al boot? Aggiungi
After=/Wants=network-online.targete una guardiaExecStartPre=. - Hai cambiato un unit file?
systemctl daemon-reload && systemctl restart <unit>. - Aggiungi
Restart=on-failurecosì il prossimo crash si annuncia invece di nascondersi.
La maggior parte dei momenti «systemd è complicato» si riduce a l’errore era nel journal fin dall’inizio. Leggilo prima di modificare l’unit file, non dopo.
— mrsaynothing
Get the next one by email
One email per post. No spam, no algorithms.
what is this?Quantizzazione GGUF: che livello usare?
Ti piacciono gli articoli? È così che costruisco per lavoro. assumimi