Docker Compose in homelab: hardening pratico senza complicarsi la vita
2026-06-15T06:00:15Z
Docker Compose è spesso il punto in cui un homelab smette di essere un laboratorio e diventa una piccola infrastruttura reale: reverse proxy, dashboard, wiki, monitoring, storage fotografico, automazioni, magari qualche servizio esposto via VPN o tunnel. Funziona, ma non è una buona base su cui appoggiare servizi che contengono dati personali o credenziali.
L’obiettivo non è trasformare ogni Raspberry Pi o mini server Proxmox in un cluster enterprise. L’obiettivo è ridurre i rischi più comuni con misure semplici, ripetibili e compatibili con la vita reale di un SysAdmin: meno root dove possibile, meno segreti in chiaro, meno socket Docker esposti, reti più ordinate, backup verificabili e aggiornamenti controllati.
Il modello di rischio da tenere in testa
In Docker il confine tra container e host non va trattato come un muro invalicabile. È un buon isolamento operativo, ma un container con privilegi eccessivi, bind mount sensibili o accesso al socket Docker può diventare rapidamente un problema dell’intero host. In homelab il rischio tipico non è l’attaccante sofisticato che buca il kernel al primo colpo; molto più spesso è un pannello web lasciato esposto, una password finita nel repository Git, un’immagine non aggiornata o un container compromesso che trova /var/run/docker.sock e da lì controlla tutto.
La checklist migliore parte quindi da una domanda brutale: se questo container venisse compromesso, cosa potrebbe leggere o modificare sull’host?
1. Rootless o user namespace: scegliere una strategia
Docker supporta il rootless mode, che esegue daemon e container come utente non privilegiato. È una mitigazione molto forte per host dedicati a workload compatibili, perché riduce l’impatto di vulnerabilità nel daemon o nel runtime. Se rootless è troppo invasivo per il tuo stack, valuta almeno userns-remap. Con il remapping degli user namespace, un processo che dentro al container vede UID 0 viene mappato su un utente non privilegiato dell’host. È una soluzione meno “pulita” del rootless completo, ma spesso più facile da adottare su server esistenti.
Scelta pratica: su un host nuovo, prova rootless prima di mettere in produzione i servizi. Su un host già pieno di container, pianifica userns-remap in una finestra di manutenzione e testa bene i permessi dei volumi.
2. Non montare il socket Docker se non è indispensabile
Il file /var/run/docker.sock equivale quasi sempre a root sull’host. Un container che può parlare con il daemon può creare altri container, montare filesystem dell’host e modificare l’ambiente. Molti stack lo richiedono per comodità: reverse proxy con discovery automatica, dashboard, updater, monitor.
La regola è semplice: se puoi evitare il socket, evitalo. Se devi usarlo, preferisci un socket proxy con API filtrate e mettilo su una rete dedicata accessibile solo ai container che ne hanno davvero bisogno. ## 3. Segreti: basta password dentro al compose
Le variabili d’ambiente sono comode, ma finiscono facilmente in shell history, backup, issue tracker, repository privati e output diagnostici. Docker Compose supporta i secrets anche fuori da Swarm tramite file montati nel container. Non è una bacchetta magica, ma migliora molto la disciplina operativa.
Un pattern ragionevole è questo:
services:
app:
image: example/app:1.2.3
secrets:
- db_password
environment:
DB_PASSWORD_FILE: /run/secrets/db_password
secrets:
db_password:
file: ./secrets/db_password.txt
Poi proteggi la directory secrets/ con permessi stretti, escludila da Git e includila nei backup cifrati. ## 4. Privilegi minimi nel file compose
Molti container funzionano con opzioni più restrittive di quelle predefinite. Non applicarle alla cieca, ma tienile come baseline da testare:
services:
app:
read_only: true
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
tmpfs:
- /tmp
pids_limit: 256
mem_limit: 512m
read_only obbliga a dichiarare esplicitamente dove l’applicazione può scrivere. cap_drop: ALL rimuove capability Linux non necessarie; quando qualcosa si rompe, aggiungi solo quella specifica. no-new-privileges impedisce escalation tramite binari setuid. Limiti di memoria e processi non sono solo tuning: riducono l’impatto di bug e loop incontrollati.
5. Reti separate e porte pubblicate con criterio
Uno degli errori più frequenti è pubblicare porte sull’interfaccia sbagliata. Se un servizio deve essere raggiunto solo dal reverse proxy, non esporlo con ports:; usa una rete interna e lascia che sia il proxy a parlare con lui. Se una porta serve solo localmente, vincolala a loopback:
ports:
- "127.0.0.1:8080:80"
Dividi gli stack in reti Compose dedicate: frontend, backend, monitoring. Non è micro-segmentazione perfetta, ma impedisce a ogni container di vedere tutto per default. ## 6. Immagini: pinning, aggiornamenti e rollback
Usare latest è comodo, ma rende i rollback meno prevedibili. Per servizi importanti preferisci tag espliciti, meglio ancora digest quando la stabilità conta più della comodità. Aggiorna con una routine: pull, lettura changelog, snapshot o backup, riavvio, test HTTP e controllo log.
Watchtower e tool simili vanno bene per servizi non critici, ma su database, reverse proxy, identity provider e storage io preferisco aggiornamenti manuali o semi-automatici. ## 7. Backup dei volumi e test di restore
Il backup di un host Docker non è solo la copia di /opt/compose. Servono compose file, .env non sensibili, secrets, volumi persistenti e, per i database, dump coerenti o snapshot applicativi. Prima di aggiornare uno stack importante, genera un backup piccolo e ripristinabile.
Checklist minima mensile:
- elenco degli stack e dei volumi;
- dump dei database prima degli upgrade;
- copia cifrata offsite dei dati importanti;
- test di restore su una VM o su un percorso alternativo;
- verifica che i secrets non siano finiti in repository o log.
Il test di restore è la parte che distingue un backup da una speranza.
8. Logging e rilevamento dei problemi
Configura la rotazione dei log, altrimenti un container rumoroso può riempire il disco. A livello daemon puoi impostare json-file con max-size e max-file, oppure usare un driver centralizzato. Per un homelab basta spesso una combinazione di Uptime Kuma, logrotate, alert disco e controllo periodico di docker events o dei log del reverse proxy.
Non serve costruire un SOC in cantina. Serve sapere quando un servizio è esposto, quando fallisce un login, quando un disco si riempie e quando un container ricomincia a riavviarsi in loop.
Conclusione
Hardening Docker Compose non significa rendere tutto scomodo. Significa scegliere default migliori: niente socket Docker dove non serve, segreti fuori dai compose, permessi ridotti, reti separate, immagini aggiornate con criterio e backup provati. In un homelab serio queste pratiche valgono più dell’ennesimo servizio installato nel weekend, perché rendono l’ambiente più prevedibile quando qualcosa va storto.
La regola operativa che uso è semplice: ogni nuovo stack deve dichiarare cosa espone, dove scrive, quali segreti usa, come si aggiorna e come si ripristina. Se queste cinque risposte sono chiare, Docker Compose resta uno strumento leggero; se non lo sono, diventa debito tecnico con una bella interfaccia web.
Fonti principali: documentazione Docker su rootless mode, user namespace remapping e Compose secrets; discussioni recenti della community homelab/self-hosted su socket Docker, rootless e gestione dei privilegi.