1,725 25/03/2026 07/04/2026 7 min

Su server di produzione, un dettaglio spesso ignorato è la gestione delle directory temporanee e dei log persistenti. Se systemd-tmpfiles o journald sono impostati male, il problema emerge solo dopo il reboot: cache mancante, log persi, servizi che partono ma falliscono al primo accesso in scrittura.

Qui vediamo una configurazione concreta per una macchina Linux che ospita un’app web o un servizio interno. L’obiettivo è semplice: directory create al boot, permessi stretti, log consultabili e nessuna sorpresa dopo il riavvio.

Prerequisiti

Serve un host Linux con systemd, accesso root o sudo, e un servizio che scrive in una cache locale, per esempio /var/cache/app e /var/log/app. Esegui i test su una VM o su un nodo di staging prima di toccare produzione.

Note: i comandi sotto sono validi su Debian, Ubuntu, Rocky e derivate. Cambiano i percorsi, non il principio.

Step 1: definire le directory che systemd deve creare

Il punto non è “fare una cartella”. Il punto è farla ricreare sempre con owner, gruppo e permessi giusti, anche dopo un reboot pulito o un filesystem montato in ritardo.

Crea un file dedicato in /etc/tmpfiles.d/app.conf invece di infilare tutto negli script di avvio. Così la configurazione resta dichiarativa e auditabile.

# /etc/tmpfiles.d/app.conf
# Tipo  Path             Mode  User   Group  Age  Argument
d     /var/cache/app     0750  app    app    -    -
d     /var/log/app       0750  app    adm    -    -

# Output: nessun output se il file è solo scritto. La verifica arriva al passo successivo.

Perché funziona: d crea la directory se manca, ma non cancella i contenuti esistenti. È la scelta giusta per cache e log.

Warning: non usare z o Z se non sai bene cosa stai facendo. Possono cambiare i permessi in modo più aggressivo del necessario.

Step 2: testare tmpfiles prima del reboot

Prima di riavviare, forza systemd a rileggere la regola e applicarla subito. Questo evita il classico errore “funziona dopo il reboot ma non ora”, o il contrario.

sudo systemd-tmpfiles --create /etc/tmpfiles.d/app.conf
sudo stat -c '%A %U %G %n' /var/cache/app /var/log/app

# Output: qualcosa di simile a drwxr-x--- app app /var/cache/app e drwxr-x--- app adm /var/log/app.

Se il servizio gira con utente dedicato, fai coincidere owner e gruppo con la sua identità reale. Non affidarti a root:root e poi a chmod casuali.

Note: se il servizio scrive log con gruppi diversi, considera un gruppo comune e una policy di setgid sulla directory, ma solo quando serve davvero.

Step 3: rendere journald utile in produzione senza aprire troppo

Il journal è comodo, ma in produzione va messo in sicurezza. Se resta volatile, perdi i log al reboot. Se è persistente ma troppo permissivo, regali eventi sensibili a utenti non autorizzati.

Modifica /etc/systemd/journald.conf con parametri chiari e pochi compromessi.

[Journal]
Storage=persistent
Compress=yes
Seal=yes
SystemMaxUse=500M
RuntimeMaxUse=100M
MaxRetentionSec=14day

# Output: dopo il reload, il journal conserva i log su disco e limita lo spazio usato.

Perché questi valori: Storage=persistent evita la perdita dei log al reboot. Seal=yes aumenta l’integrità. I limiti proteggono il disco da crescita incontrollata.

Warning: non impostare retention infinita su host piccoli. Il journal non deve competere con database, cache o backup locali.

Step 4: applicare hardening minimo al journal

La sicurezza non è un extra. Un journal persistente va letto solo da chi deve fare troubleshooting. Il resto degli utenti non deve vedere errori applicativi, path, token o dettagli interni.

Controlla i gruppi locali e, se serve, limita l’accesso a systemd-journal. Su sistemi ben amministrati, bastano pochi operatori fidati.

getent group systemd-journal
sudo usermod -aG systemd-journal ops1
sudo journalctl -p warning -u app.service --since today

# Output: il comando journalctl mostra solo gli eventi del servizio con priorità warning o superiore.

Se hai bisogno di un accesso ancora più stretto, usa un account di supporto separato e non il tuo utente personale.

Note: su ambienti regolati, conserva anche una copia remota dei log. Il journal locale non basta come evidenza storica.

Step 5: agganciare il servizio a tmpfiles e al journal

Un servizio robusto verifica le sue directory all’avvio e fallisce in modo chiaro se manca un prerequisito. Questo evita avvii “a metà”, più difficili da diagnosticare.

Nel file unit di systemd, usa dipendenze esplicite e una directory privata per i runtime file se l’app lo consente.

[Unit]
Description=App web interna
After=network-online.target
Wants=network-online.target
RequiresMountsFor=/var/cache/app /var/log/app

[Service]
User=app
Group=app
ExecStart=/usr/local/bin/app
Restart=on-failure
RuntimeDirectory=app
StateDirectory=app
LogsDirectory=app
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
NoNewPrivileges=yes

# Output: il servizio parte solo quando i mount richiesti sono disponibili e con una superficie d’attacco ridotta.

Perché è da produzione: ProtectSystem=strict, ProtectHome=yes e NoNewPrivileges=yes bloccano molte escalation banali. RequiresMountsFor evita corse contro directory non pronte.

Warning: PrivateTmp=yes può rompere applicazioni che si aspettano file condivisi in /tmp. Va testato, non abilitato per inerzia.

Step 6: fare un reboot simulato e leggere i log giusti

Il test vero è il reboot. Prima però, puoi simulare la sequenza di boot e trovare i punti deboli senza spegnere tutto.

sudo systemctl daemon-reload
sudo systemd-tmpfiles --create
sudo systemctl restart app.service
sudo journalctl -b -u app.service --no-pager

# Output: il servizio si riavvia senza errori di permesso o directory mancanti, e il journal mostra il boot corrente.

Dopo il reboot reale, verifica tre cose: la directory esiste, i permessi sono corretti, il journal conserva gli eventi precedenti. Se una sola delle tre fallisce, la configurazione non è ancora pronta per produzione.

Verifica finale

Fai questo controllo rapido ogni volta che aggiorni l’unit o il file tmpfiles:

  • la directory cache esiste dopo il boot;
  • owner e gruppo sono corretti;
  • il servizio scrive log senza errori;
  • il journal sopravvive al riavvio;
  • lo spazio assegnato al journal resta sotto controllo.
systemd-tmpfiles --cat-config | grep -E 'app|cache|log'
journalctl --disk-usage
ls -ld /var/cache/app /var/log/app

# Output: config visibile, uso disco del journal misurabile, directory presenti con permessi attesi.

Se vuoi un controllo più rigoroso, aggiungi un test automatico in CI o in un timer systemd che segnali la scomparsa della directory o un cambio di ownership.

Troubleshooting

1) “Failed to create directory /var/cache/app: Permission denied”

Causa: la regola tmpfiles non viene applicata con owner giusto, oppure il path è su un mount con permessi incoerenti.

Fix:

sudo systemd-tmpfiles --create /etc/tmpfiles.d/app.conf
sudo chown -R app:app /var/cache/app
sudo restorecon -Rv /var/cache/app 2>/dev/null || true

2) “System journal is not persistent”

Causa: Storage=persistent non è attivo, oppure /var/log/journal non esiste con i permessi corretti.

Fix:

sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald

3) “app.service: Failed at step CHDIR spawning /usr/local/bin/app: No such file or directory”

Causa: il servizio usa una working directory che tmpfiles non ha creato, o che si monta troppo tardi.

Fix:

systemctl cat app.service
sudo systemd-analyze verify /etc/systemd/system/app.service
sudo systemctl daemon-reload

Conclusione

La parte sottovalutata non è il servizio. È l’ambiente che lo circonda: directory, permessi, journal e dipendenze di boot. Se questi dettagli sono solidi, molti incidenti spariscono prima ancora di arrivare in produzione.

Il prossimo passo concreto è trasformare questa base in un controllo automatico. Un timer systemd che verifica ownership, spazio del journal e presenza delle directory ti evita almeno metà dei ticket notturni.

Note: in infrastrutture serie, la differenza tra “funziona” e “regge il reboot” è quasi sempre nascosta in tmpfiles o journald.