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.