Filesystem, utenti, processi, dischi, systemd, log, SSH, performance e tutto quello che serve per non farsi fregare dal server alle 3 di notte.
Perché "ho letto la man page" non l'ha mai detto nessuno in produzione.
"È già tutto scritto nella documentazione" — sì, e ci vogliono 4 ore per trovare il pezzo che serve.
Dove vive cosa, e perché non mettere tutto in /tmp
Linux ha un unico albero di directory che parte da / (root). Non esistono C:\ e D:\: tutto è montato da qualche parte nell'albero. I dischi aggiuntivi vengono "montati" come cartelle — /mnt/dati, /home, /var possono stare su dischi fisici diversi, ma li vedi tutti nello stesso posto.
df -h e du -sh /var/* prima di essere svegliato alle 3 di notte da un alert. Se il disco è pieno non riesci nemmeno a fare il login SSH in certi casi.
# quanto spazio hai df -h # tutti i filesystem montati, human-readable df -h / # solo il root # cosa occupa spazio du -sh /var/log/* # dimensione di ogni file/dir in /var/log du -sh /* 2>/dev/null # top-level, ignora errori permessi du -sh /var/* | sort -h # ordina per dimensione # navigazione rapida ls -lah /etc/ # lista dettagliata con dimensioni human-readable file /dev/sda # che tipo di file è? stat /etc/passwd # inode, permessi, timestamp completi readlink -f /bin/python3 # risolvi symlink fino alla destinazione reale lsof /var/log/syslog # chi ha aperto questo file? fuser /var/log/syslog # quali processi usano questo file
Non sono directory reali — sono filesystem virtuali generati al volo dal kernel. Ogni numero in /proc è un PID.
cat /proc/cpuinfo # info CPU cat /proc/meminfo # info RAM dettagliate cat /proc/loadavg # load average corrente cat /proc/1/cmdline # command line del PID 1 (systemd/init) cat /proc/1/environ # variabili d'ambiente del PID 1 ls -la /proc/self/fd/ # file descriptor della shell corrente cat /sys/class/net/eth0/speed # velocità interfaccia di rete cat /sys/block/sda/size # dimensione disco in settori da 512 byte
Tutto gira con un UID. Anche root è solo UID 0.
# creare un utente con home e shell useradd -m -s /bin/bash mario passwd mario # modificare utente esistente usermod -aG sudo mario # aggiungi a gruppo sudo usermod -s /bin/bash mario # cambia shell usermod -l newname mario # rinomina utente # info utente id mario # uid, gid, gruppi getent passwd mario # riga da /etc/passwd groups mario # lista gruppi last mario # ultimi login w # chi è loggato adesso # bloccare/sbloccare account usermod -L mario # lock usermod -U mario # unlock userdel -r mario # elimina utente + home
# gestione gruppi groupadd developers groupdel developers gpasswd -a mario developers # aggiungi gpasswd -d mario developers # rimuovi # file chiave /etc/passwd # utenti: nome:x:uid:gid:info:home:shell /etc/shadow # hash password (solo root) /etc/group # gruppi: nome:x:gid:membri /etc/sudoers # chi può fare sudo (usa visudo!) # sudo best practice visudo # modifica /etc/sudoers con validazione # mario ALL=(ALL:ALL) ALL # mario ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx
/etc/sudoers direttamente con nano. Un errore di sintassi e perdi l'accesso sudo. Usa sempre visudo — fa la validazione.
Ogni file ha 3 set di permessi: owner, group, others. Si leggono da sinistra a destra.
# chmod numerica (più veloce) chmod 755 script.sh # rwxr-xr-x (standard per script/dir) chmod 644 config.conf # rw-r--r-- (standard per file config) chmod 600 .env # rw------- (solo owner — file segreti) chmod 700 /root # rwx------ (solo root) # chmod simbolica (più leggibile) chmod u+x script.sh # aggiungi execute all'owner chmod g-w file.txt # rimuovi write al group chmod o=r file.txt # others: solo read chmod a+x /usr/local/bin/tool # tutti: aggiungi execute chmod -R 755 /var/www/html # ricorsivo # chown chown mario file.txt # cambia owner chown mario:developers file.txt # owner e group chown -R www-data:www-data /var/www # ricorsivo # umask — maschera default per nuovi file umask # mostra umask corrente (tipico: 022) # umask 022 → file creati con 644, dir con 755 # umask 027 → file con 640, dir con 750 (più restrittivo)
Quelli che tutti dimenticano finché non servono.
| Bit | Su file | Su directory | Esempio |
|---|---|---|---|
| SUID (4xxx) | Esegue come owner del file (anche se sei un altro utente) | Ignorato | /usr/bin/passwd — gira come root per cambiare /etc/shadow |
| SGID (2xxx) | Esegue come group del file | Nuovi file ereditano il group della dir | chmod 2755 /shared — tutti i file in /shared appartengono al gruppo della dir |
| Sticky (1xxx) | Obsoleto | Solo il proprietario può cancellare i propri file | /tmp — tutti scrivono, nessuno cancella i file degli altri |
chmod u+s /usr/bin/myprog # SUID chmod g+s /shared # SGID su directory chmod +t /tmp # sticky bit # ls -la mostrerà: -rwsr-xr-x (SUID), drwxrwsr-x (SGID), drwxrwxrwt (sticky) # trova tutti i SUID nel sistema — utile per audit sicurezza find / -perm -4000 -type f 2>/dev/null
Tutto è un processo. Anche quello che sta bloccando il tuo server adesso.
ps aux # tutti i processi ps aux | grep nginx # filtra per nome ps -ef --forest # albero gerarchico padre-figlio pgrep nginx # solo i PID di nginx pgrep -l nginx # PID + nome pidof nginx # PID(s) di nginx top # interattivo, tasto 1 per CPU per core htop # come top ma bello (installa: apt install htop)
# i segnali più usati kill -15 1247 # SIGTERM — "per favore chiudi" (graceful) kill -9 1247 # SIGKILL — "muori adesso" (non interceptabile) kill -1 1247 # SIGHUP — "ricarica config" (per daemon come nginx) kill -2 1247 # SIGINT — equivalente Ctrl+C kill -19 1247 # SIGSTOP — sospendi (come Ctrl+Z) kill -18 1247 # SIGCONT — riprendi # per nome pkill nginx # SIGTERM a tutti i processi "nginx" pkill -9 python3 # SIGKILL a tutti i python3 killall nginx # alternativa a pkill # usa sempre -15 prima di -9 # -9 non dà al processo tempo di pulire — file aperti, lock, connessioni
# background / foreground comando & # lancia in background Ctrl+Z # sospendi processo corrente bg # riprendi in background fg # riporta in foreground jobs # lista job correnti della shell nohup comando & # sopravvive alla chiusura della shell (SIGHUP ignorato) disown %1 # sgancia job 1 dalla shell (non riceve più SIGHUP) # priorità (nice: -20 = max priorità, 19 = min) nice -n 10 comando # lancia con nice 10 (meno priorità) renice -n 5 -p 1247 # cambia nice di processo esistente renice -n -5 -p 1247 # aumenta priorità (solo root può scendere sotto 0)
Il gestore di tutto. O quasi tutto. Ok, di tutto.
systemd è il PID 1 — il primo processo che parte dopo il kernel. Gestisce tutto il ciclo di vita dei servizi, il boot, il logging (journald), i mount, i socket, i timer. Se un processo non è gestito da systemd su un server moderno, probabilmente è un errore.
# stato e controllo servizi systemctl status nginx # stato dettagliato + ultimi log systemctl start nginx systemctl stop nginx systemctl restart nginx # stop + start (interrompe connessioni) systemctl reload nginx # ricarica config senza interrompere (se supportato) systemctl enable nginx # parte automaticamente al boot systemctl disable nginx # non parte al boot systemctl enable --now nginx # enable + start in una sola riga systemctl mask nginx # impedisce completamente l'avvio (anche manuale) # overview sistema systemctl list-units --type=service # tutti i servizi attivi systemctl list-units --failed # servizi falliti systemctl list-unit-files --type=service # tutti + stato enable/disable systemctl is-active nginx # active / inactive / failed systemctl is-enabled nginx # enabled / disabled # boot e target systemctl get-default # target di default (graphical.target / multi-user.target) systemctl set-default multi-user.target # server senza GUI systemctl reboot systemctl poweroff systemctl rescue # modalità recovery
Per far girare un'applicazione custom come servizio. I file vanno in /etc/systemd/system/.
# dopo aver creato/modificato il file systemctl daemon-reload # rileggi tutti i file unit systemctl enable --now myapp systemctl status myapp
journalctl -u nginx # log del servizio nginx journalctl -u nginx -f # segui in tempo reale journalctl -u nginx --since "1 hour ago" journalctl -u nginx --since "2024-01-15 10:00" --until "2024-01-15 11:00" journalctl -u nginx -n 50 # ultime 50 righe journalctl -p err # solo errori (err, warning, info, debug) journalctl -b # log dal boot corrente journalctl -b -1 # log dal boot precedente journalctl --disk-usage # quanto spazio occupano i log journalctl --vacuum-time=7d # elimina log più vecchi di 7 giorni
Perché "disco pieno" è l'errore più evitabile e il più comune
# vedere dischi e partizioni lsblk # albero dischi/partizioni/mount lsblk -f # + filesystem e UUID fdisk -l # dettagli partizioni (richiede root) blkid # UUID e tipo filesystem di ogni device parted -l # alternativa moderna a fdisk # nomi tipici # /dev/sda → primo disco SCSI/SATA/USB # /dev/sda1 → prima partizione # /dev/nvme0n1 → primo SSD NVMe # /dev/nvme0n1p1 → prima partizione NVMe # /dev/vda → disco virtuale (VM KVM/QEMU)
# partizionare (interattivo) fdisk /dev/sdb # MBR (legacy, <2TB) gdisk /dev/sdb # GPT (moderno, >2TB, UEFI) parted /dev/sdb # alternativa non-interattiva possibile # creare filesystem mkfs.ext4 /dev/sdb1 # ext4 (standard Linux) mkfs.xfs /dev/sdb1 # XFS (ottimo per grandi file, usato da RHEL) mkfs.vfat /dev/sdb1 # FAT32 (USB compatibilità universale) mkswap /dev/sdb2 # partizione swap # montare mount /dev/sdb1 /mnt/dati umount /mnt/dati mount -o remount,ro /mnt/dati # rimonta in sola lettura # montaggio permanente in /etc/fstab # UUID=abc123 /mnt/dati ext4 defaults,nofail 0 2 # usa UUID (non /dev/sdb1) — i device name cambiano al riavvio mount -a # monta tutto da fstab (testa prima di riavviare)
mount -a prima del reboot. L'opzione nofail fa sì che il boot continui anche se il mount fallisce.
LVM aggiunge un layer di astrazione tra dischi fisici e filesystem: puoi ridimensionare, aggiungere dischi, fare snapshot senza downtime.
# creare LVM da zero pvcreate /dev/sdb /dev/sdc # inizializza PV vgcreate vgdata /dev/sdb /dev/sdc # crea VG lvcreate -L 100G -n lv_home vgdata # LV da 100GB lvcreate -l 100%FREE -n lv_data vgdata # LV con tutto lo spazio rimasto mkfs.ext4 /dev/vgdata/lv_home mount /dev/vgdata/lv_home /home # espandere un LV (senza downtime su ext4/xfs) lvextend -L +50G /dev/vgdata/lv_home # aggiungi 50GB resize2fs /dev/vgdata/lv_home # ridimensiona filesystem ext4 xfs_growfs /home # ridimensiona filesystem xfs # stato pvs # physical volumes vgs # volume groups lvs # logical volumes # snapshot LVM (backup istantaneo) lvcreate -s -L 10G -n lv_home_snap /dev/vgdata/lv_home
apt, dnf, e perché non fare curl | bash in produzione
apt update # aggiorna lista pacchetti apt upgrade # aggiorna pacchetti installati apt full-upgrade # upgrade + rimuovi obsoleti apt install nginx apt install nginx=1.24.0-1 # versione specifica apt remove nginx # rimuovi (lascia config) apt purge nginx # rimuovi + config apt autoremove # rimuovi dipendenze orfane apt search nginx apt show nginx # dettagli pacchetto apt list --installed dpkg -l | grep nginx # basso livello dpkg -L nginx # file installati dal pacchetto dpkg -S /usr/sbin/nginx # quale pacchetto ha installato questo file
dnf check-update dnf update dnf install nginx dnf remove nginx dnf search nginx dnf info nginx dnf list installed dnf provides /usr/sbin/nginx # quale pacchetto installa questo file rpm -ql nginx # file installati (basso livello) rpm -qa # tutti i pacchetti installati # repository dnf repolist dnf config-manager --add-repo URL
# aggiungere un repo di terze parti (es. Docker su Ubuntu) curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg echo "deb [signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list apt update && apt install docker-ce # i file dei repo stanno in /etc/apt/sources.list # file principale /etc/apt/sources.list.d/*.list # file aggiuntivi (uno per repo)
I log ci sono sempre. Il problema è trovarli prima che il server esploda.
| File | Contiene |
|---|---|
/var/log/syslog o messages | Log di sistema generale (kernel, daemon, servizi) |
/var/log/auth.log o secure | Login, sudo, SSH, autenticazione |
/var/log/kern.log | Messaggi kernel (driver, hardware, OOM killer) |
/var/log/dpkg.log | Installazioni/rimozioni pacchetti |
/var/log/nginx/access.log | Ogni richiesta HTTP ricevuta da nginx |
/var/log/nginx/error.log | Errori nginx (502, config sbagliata, permessi) |
/var/log/postgresql/ | Log PostgreSQL (query lente, errori) |
journalctl | Tutto quello gestito da systemd (binary journal) |
# seguire in tempo reale tail -f /var/log/syslog tail -f /var/log/nginx/error.log tail -n 100 /var/log/auth.log # cercare pattern grep "Failed password" /var/log/auth.log grep -i error /var/log/syslog | tail -50 grep "Jan 15" /var/log/syslog grep -v "healthcheck" /var/log/nginx/access.log # escludi pattern # chi sta tentando di entrare nel tuo server grep "Failed password" /var/log/auth.log | awk '{print $11}' | sort | uniq -c | sort -rn | head # ultime connessioni SSH riuscite grep "Accepted" /var/log/auth.log last # tutti i login (ssh + console) lastb # login falliti lastlog # ultimo login per ogni utente
# configurazione in /etc/logrotate.d/myapp /var/log/myapp/*.log { daily # ruota ogni giorno rotate 14 # mantieni 14 file compress # comprimi con gzip delaycompress # comprimi il precedente, non il corrente missingok # non fare errore se il file non esiste notifempty # non ruotare se vuoto create 640 www-data adm # crea nuovo file con questi permessi postrotate systemctl reload myapp # riapri il file di log endscript } # testare logrotate logrotate -d /etc/logrotate.d/myapp # dry run logrotate -f /etc/logrotate.d/myapp # forza rotazione ora
Capire perché è lento prima di dire "serve più RAM"
uptime 14:23:01 up 42 days, load average: 0.52, 1.23, 2.45 # 1min 5min 15min
nproc # numero di CPU/core disponibili lscpu # dettagli architettura CPU mpstat 1 # utilizzo CPU per core ogni secondo top # premi 1 per vedere ogni core, P per ordinare per CPU pidstat -u 1 # CPU per processo ogni secondo
free -h total used free shared buff/cache available Mem: 15Gi 5.2Gi 2.1Gi 420Mi 8.1Gi 9.6Gi Swap: 2Gi 100Mi 1.9Gi # "available" è quello che conta davvero — include buff/cache liberabile # "free" basso ma "available" alto = normale, Linux usa la RAM per cache vmstat 1 # virtual memory stats ogni secondo cat /proc/meminfo # dettagli completi # top memory consumers ps aux --sort=-%mem | head -10 smem -r -k | head # se installato, più accurato di ps
grep -i "oom\|killed process" /var/log/kern.log o journalctl -k | grep -i oom. Non è un crash del sistema — è il kernel che ha scelto quale processo sacrificare. Soluzioni: aggiungere swap, aumentare RAM, o limitare i processi con cgroups.
iostat -x 1 # I/O per disco ogni secondo # %util vicino a 100% = disco saturo # await alto = latenza alta (disco lento o sovraccarico) iotop # chi sta scrivendo/leggendo (apt install iotop) iotop -o # mostra solo processi con I/O attivo # velocità disco dd if=/dev/zero of=/tmp/test bs=1G count=1 oflag=direct # write speed dd if=/tmp/test of=/dev/null bs=1G count=1 iflag=direct # read speed # statistiche I/O per processo pidstat -d 1
ss -tuln # porte in ascolto (t=TCP, u=UDP, l=listening, n=no DNS) ss -tp # connessioni TCP con PID ss -s # statistiche sommarie netstat -i # statistiche per interfaccia (bytes in/out, errori) ip -s link # stesso, alternativa moderna sar -n DEV 1 # traffico per interfaccia ogni secondo nload # grafico banda in tempo reale (apt install nload)
Il tuo coltellino svizzero per tutto quello che è remoto
# genera coppia di chiavi (fai questa sul tuo PC locale) ssh-keygen -t ed25519 -C "mario@laptop" # moderno, consigliato ssh-keygen -t rsa -b 4096 -C "mario@laptop" # se il server è vecchio # chiave privata: ~/.ssh/id_ed25519 (NON condividere MAI) # chiave pubblica: ~/.ssh/id_ed25519.pub (questa metti sul server) # copiare la chiave pubblica sul server ssh-copy-id -i ~/.ssh/id_ed25519.pub mario@server.esempio.it # oppure manualmente: cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keys # ~/.ssh/config — risparmia digitazione cat ~/.ssh/config Host produzione HostName server.esempio.it User mario IdentityFile ~/.ssh/id_ed25519 Port 22 Host jumphost HostName bastion.esempio.it User mario Host prod-interno HostName 192.168.1.50 User root ProxyJump jumphost # ora puoi fare semplicemente ssh produzione ssh prod-interno # passa automaticamente per jumphost
# /etc/ssh/sshd_config — modifiche consigliate Port 2222 # cambia porta (riduce rumore nei log) PermitRootLogin no # root non si logga via SSH PasswordAuthentication no # solo chiavi, niente password PubkeyAuthentication yes AuthorizedKeysFile .ssh/authorized_keys MaxAuthTries 3 ClientAliveInterval 300 # keep-alive ogni 5 minuti ClientAliveCountMax 2 AllowUsers mario deploy # whitelist utenti (opzionale) X11Forwarding no # disabilita X forwarding se non serve sshd -t # valida la configurazione PRIMA di riavviare systemctl reload ssh # ricarica (non perdere la sessione corrente!)
PasswordAuthentication no, verifica che la tua chiave pubblica sia in ~/.ssh/authorized_keys e che funzioni. Altrimenti ti chiudi fuori dal server. Mantieni sempre una sessione SSH aperta mentre fai modifiche a sshd_config.
# scp — copia semplice scp file.txt mario@server:/tmp/ # locale → remoto scp mario@server:/var/log/syslog . # remoto → locale scp -r cartella/ mario@server:/opt/ # ricorsivo scp -P 2222 file.txt mario@server:/tmp/ # porta personalizzata # rsync — sincronizzazione intelligente (trasferisce solo differenze) rsync -avz /var/www/ mario@server:/var/www/ # sync directory rsync -avz --delete /backup/ mario@server:/backup/ # --delete rimuove file non presenti in origine rsync -avz --exclude '*.log' /opt/app/ mario@server:/opt/app/ rsync -avz -e "ssh -p 2222" /dir/ mario@server:/dir/ # porta custom rsync --dry-run -avz /dir/ mario@server:/dir/ # simula, non trasferisce
La connessione cade, il processo muore. Con tmux le sessioni sopravvivono alla disconnessione.
tmux new -s produzione # nuova sessione chiamata "produzione" tmux ls # lista sessioni attive tmux attach -t produzione # ricollegati # tasti dentro tmux (tutti preceduti da Ctrl+B) # Ctrl+B d → detach (sessione rimane attiva) # Ctrl+B c → nuova finestra # Ctrl+B n/p → finestra successiva/precedente # Ctrl+B % → split verticale # Ctrl+B " → split orizzontale # Ctrl+B frecce → muoviti tra i pannelli # Ctrl+B [ → modalità scroll (q per uscire)
Quello che dovresti fare su ogni nuovo server prima di esporre qualcosa
Urgente
apt update && apt upgradeImportante
apt install fail2ban # /etc/fail2ban/jail.local (NON modificare jail.conf) [DEFAULT] bantime = 1h # banna per 1 ora findtime = 10m # finestra di osservazione maxretry = 5 # tentativi prima del ban [sshd] enabled = true port = 2222 # se hai cambiato porta logpath = /var/log/auth.log [nginx-http-auth] enabled = true systemctl enable --now fail2ban # gestire ban fail2ban-client status # panoramica fail2ban-client status sshd # dettagli jail sshd fail2ban-client set sshd unbanip 1.2.3.4 # unban IP fail2ban-client banned # tutti gli IP bannati
apt install unattended-upgrades dpkg-reconfigure unattended-upgrades # configura interattivamente # /etc/apt/apt.conf.d/50unattended-upgrades # Unattended-Upgrade::Allowed-Origins { "${distro_id}:${distro_codename}-security"; }; # Unattended-Upgrade::Mail "admin@esempio.it"; # Unattended-Upgrade::Automatic-Reboot "false"; # verifica che funzioni unattended-upgrade --dry-run --debug
# chi può fare sudo grep -v '^#' /etc/sudoers | grep -v '^$' grep -r '' /etc/sudoers.d/ # utenti con UID 0 (root) — dovrebbe essere solo root awk -F: '$3==0' /etc/passwd # utenti con shell di login grep -v '/nologin\|/false' /etc/passwd | grep -v '^#' # porte aperte verso l'esterno ss -tuln nmap -sV localhost # file SUID (potenziale escalation) find / -perm -4000 -type f 2>/dev/null # login falliti recenti lastb | head -20 grep "Failed password" /var/log/auth.log | awk '{print $11}' | sort | uniq -c | sort -rn | head
Perché farlo a mano tre volte quando puoi automatizzarlo una volta
# sintassi: minuto ora giorno-mese mese giorno-settimana comando # m h dom month dow command # 0-59 0-23 1-31 1-12 0-7(0=7=dom) crontab -e # modifica crontab dell'utente corrente crontab -l # mostra crontab corrente crontab -u mario -e # crontab di un altro utente (da root) # esempi 0 2 * * * /usr/local/bin/backup.sh # ogni notte alle 2:00 */5 * * * * /usr/local/bin/check.sh # ogni 5 minuti 0 9 * * 1-5 /usr/local/bin/report.sh # lun-ven alle 9:00 @reboot /usr/local/bin/startup.sh # solo al boot @daily /usr/local/bin/daily.sh # una volta al giorno 0 */6 * * * /usr/local/bin/ogni6ore.sh # ogni 6 ore # redirigere l'output (altrimenti arriva per email o si perde) 0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1 # crontab di sistema /etc/crontab # ha un campo "utente" in più /etc/cron.d/ # un file per job (consigliato per applicazioni) /etc/cron.daily/ # script eseguiti ogni giorno /etc/cron.hourly/ # ogni ora
Più potente di cron: supporta dipendenze, logging integrato in journald, persistenza (se il server era spento, esegue al riavvio).
systemctl daemon-reload systemctl enable --now backup.timer systemctl list-timers # tutti i timer attivi e quando scattano journalctl -u backup.service # log dell'esecuzione
Quando le cose vanno storte (e vanno sempre storte)
# 1. stato generale uptime # load average, da quanto è su free -h # RAM disponibile df -h # spazio disco # 2. servizi falliti systemctl list-units --failed # 3. errori recenti journalctl -p err -b # errori dal boot corrente dmesg | tail -20 # messaggi kernel recenti # 4. chi sta usando le risorse top # CPU e RAM in tempo reale iotop -o # I/O disco # 5. connessioni di rete ss -tuln # porte in ascolto ss -tp state established # connessioni attive
# strace — cosa sta facendo un processo (system call) strace -p 1247 # attach a processo esistente strace -p 1247 -e trace=file # solo syscall su file strace -p 1247 -e trace=network # solo syscall di rete strace comando 2>&1 | head -50 # studia l'avvio di un comando # lsof — file aperti lsof -p 1247 # file aperti dal PID 1247 lsof -u mario # file aperti dall'utente mario lsof -i :80 # chi usa la porta 80 lsof -i TCP:1:1024 # tutte le porte privilegiate in uso # dmesg — messaggi kernel dmesg -T # con timestamp leggibili dmesg -T | grep -i error dmesg -T | grep -i oom # OOM killer dmesg -T | grep -i "I/O error" # errori disco # trovare cosa occupa una porta ss -tlnp | grep :80 fuser -n tcp 80 # PID che usa porta 80 # diagnostica file system fsck /dev/sda1 # check filesystem (solo smontato!) smartctl -a /dev/sda # salute disco SMART (apt install smartmontools) badblocks -v /dev/sda # cerca settori danneggiati (lento)
| Sintomo | Causa probabile | Dove guardare |
|---|---|---|
| Server non risponde via SSH | Disco pieno → sshd non riesce a scrivere | df -h, /var/log occupazione |
| Load average altissimo | Processo in loop, I/O wait, OOM | top, iotop, dmesg | grep oom |
| Servizio non parte | Porta già in uso, config sbagliata, permessi | systemctl status servizio, journalctl -u servizio |
| Permission denied | Owner/group sbagliato, SELinux/AppArmor | ls -la, id, aa-status |
| Cron non esegue | PATH diverso, nessun output, utente sbagliato | /var/log/syslog | grep CRON, testa il comando manualmente |
| Disco pieno ma df mostra spazio | Inode esauriti, file cancellato ma ancora aperto | df -i (inode), lsof | grep deleted |
| Processo zombie | Il processo padre non fa wait() | ps -ef --forest, riavvia il padre |
Hai cancellato un file di log enorme, df dice ancora "pieno". Motivo: il processo che aveva il file aperto lo tiene ancora in uso — lo spazio viene liberato solo quando il processo rilascia il file descriptor.
# trova file cancellati ma ancora aperti (quindi ancora occupano spazio) lsof | grep deleted nginx 892 www-data 10w REG ... /var/log/nginx/access.log (deleted) # soluzioni systemctl reload nginx # riapre il file di log (preferito) kill -HUP 892 # oppure SIGHUP al processo # in extremis : > /proc/892/fd/10 # svuota il file tramite FD (senza riavviare)
Il 90% dei problemi rientra in queste categorie: disco pieno, permessi sbagliati, servizio non avviato/crashato, porta già in uso. Prima di fare cose complesse, controlla queste quattro cose nell'ordine. Poi leggi i log. Poi chiedi a Google con l'errore esatto copiato e incollato. Poi considera che forse il problema è il DNS.