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. Il problema è che molti stack nascono con un docker compose up -d, qualche volume montato in fretta e privilegi più larghi del necessario. 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. Non è però sempre trasparente: networking, porte basse, filesystem e integrazioni particolari possono richiedere adattamenti.

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. Per esempio, un reverse proxy non dovrebbe avere automaticamente lo stesso livello di accesso di un tool amministrativo.

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. La cosa importante è evitare che il compose diventi il tuo password manager.

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. In ambienti piccoli questa singola abitudine evita molte sorprese.

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. L’automazione deve accelerare il lavoro, non toglierti il punto di controllo.

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.