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.
Commenti (0)
Nessun commento ancora.
Segnala contenuto
Elimina commento
Eliminare definitivamente questo commento?
L'azione non si può annullare.