Skip to main content
  1. Concetti/

Docker Volumes

·5 mins
Alessio Barnini
Author
Alessio Barnini
Table of Contents

Cos'e
#

Un volume Docker e uno spazio di archiviazione persistente gestito da Docker, separato dal filesystem del container. I dati in un volume sopravvivono all'eliminazione del container, al contrario di quelli scritti nel layer del container che vengono persi.

TL;DR
#

# Senza volume: i dati muoiono col container
docker run postgres   →  container eliminato  →  dati persi

# Con volume: i dati persistono
docker run -v db-data:/var/lib/postgresql/data postgres
           →  container eliminato  →  dati nel volume intatti
           →  nuovo container con stesso volume  →  dati recuperati

Un volume e come un hard disk esterno collegato al container: puoi staccare il container, il disco resta.


Come funziona
#

Docker Host (server Hetzner)
├── /var/lib/docker/volumes/
│     ├── n8n_data/_data/              ← dati reali qui
│     ├── n8n_postgres_data/_data/
│     └── postgres_rag_data/_data/
└── Container
      └── /home/node/.n8n              ← container vede questo path
            └── (montato su n8n_data)  ← ma i dati vivono nel volume

Il container legge e scrive su un path interno (es. /var/lib/postgresql/data), Docker si occupa di redirigere quel traffico verso il volume sul filesystem dell'host.

Tipi di volume
#

Named volumeBind mountVolume anonimo
Persistentesisisi (ma perdi il riferimento)
Path visibile sull'hostno — vive in /var/lib/docker/volumes/si — e un path reale tuono
Editabile dall'hostno (serve docker exec)si — editor, git, tuttono
Portabile tra serversidipende dal path assolutono
Usato perdati che possiede il containerconfig che controlli tuda evitare
Usato per (casi reali)DB (postgres, mysql), log applicativi, queue, stato interno generato dal containerfile di config che editi spesso, certificati, .env, config che vuoi sotto gitniente — usa named volume

Esistono tre modi per montare dati in un container:

Named volume
#

gestito da Docker, nome esplicito. E il tipo consigliato per la produzione.

volumes:
  - app_data:/var/lib/app/data

Naming convention Docker: {nome_cartella_progetto}_{nome_volume}

Il path /var/lib/app/data esiste SOLO dentro il container. Sull'host i dati vivono in /var/lib/docker/volumes/{progetto}_app_data/_data/. Non trovi quella cartella sul tuo host al path del container — un named volume non crea mai un path sull'host: crea storage interno a Docker e lo espone al container al path indicato.

Per modificare i file dentro un named volume devi entrare nel container con docker exec.

Bind mount
#

mappa un path reale dell'host dentro il container.

volumes:
  - ./config/app.conf:/etc/app/app.conf

./config/app.conf e un file reale sul tuo host. Il container lo legge al path interno. Lo editi con il tuo editor sull'host senza mai aprire il container. Va sotto git, e portabile, e leggibile da fuori.

Chi possiede il dato — regola per scegliere:

DomandaRispostaUsa
Il dato lo genera o gestisce il container?DB, observability, stato appNamed volume
Il dato lo gestisci tu dall'esterno?Config, certificati, .envBind mount
Vuoi modificare i file senza ricostruire l'immagine?Codice sorgente in sviluppoBind mount
Important

I volumi non hanno niente a che fare con la ricostruzione dell'immagine. docker build serve solo quando cambi il Dockerfile. Con i volumi (named o bind) i dati persistono e sono accessibili senza mai ricostruire niente — la differenza e solo dove vivono i file e chi li modifica.

In sviluppo il bind mount sul codice sorgente e comune: modifichi i file sull'host, il container li vede subito senza restart.

- ./src:/app/src    # sviluppo: live reload senza rebuild

In produzione il bind mount si usa solo per file di configurazione che vuoi gestire dall'host — non per il codice, che e dentro l'immagine.

La differenza visiva:

Host filesystem                                  Container filesystem

/var/lib/docker/volumes/
  myproject_app_data/_data/     ←─named volume─→  /var/lib/app/data/
    file.db                                          file.db  ← vedi qui
    logs/                                            logs/
    (modifichi con docker exec)

./config/
  app.conf                      ←─bind mount────→  /etc/app/app.conf
  (modifichi sull'host)                             (stessa cosa, path diverso)

Se un file e in un named volume e non lo vedi sull'host al path del container, non e un bug — e il comportamento corretto. Cercalo in /var/lib/docker/volumes/ oppure entra nel container.

Volume anonimo
#

nome generato da Docker (hash). Da evitare in produzione perche non e identificabile.

volumes:
  - /var/lib/postgresql/data   # no nome = anonimo

Sintassi docker run vs docker-compose
#

La sintassi -v in docker run e' equivalente alla dichiarazione in compose, ma inline — non esiste una sezione volumes: separata.

# Bind mount — path assoluto host:path container
docker run -v /home/user/config:/etc/app/config image

# Bind mount — path relativo con $(pwd)
docker run -v "$(pwd):/home/claude/workspace" image

# Named volume — nome:path container
docker run -v mydata:/var/lib/data image

# Piu' volumi insieme
docker run \
  -v "$(pwd):/home/claude/workspace" \
  -v "$HOME/.myapp:/home/claude/.myapp" \
  -v mydata:/var/lib/data \
  image
Note

Con docker run i named volume vengono creati automaticamente se non esistono. Non hanno il prefisso del progetto come in compose — il nome e' esattamente quello dichiarato.

La stessa logica bind mount / named volume si applica in entrambi i contesti — cambia solo la sintassi di dichiarazione.

Naming e prefissi
#

Docker compone il nome fisico del volume unendo il nome del progetto e il nome dichiarato nel compose. Il nome del progetto corrisponde alla cartella che contiene il docker-compose.yml.

cartella del progetto: myproject/
volume dichiarato nel compose: app_data
nome fisico su Docker: myproject_app_data

→ visibile in: /var/lib/docker/volumes/myproject_app_data/
→ listabile con: docker volume ls | grep myproject

Se non conosci il nome esatto, ricavalo dalla cartella del progetto + il nome nel compose:

# Struttura compose
volumes:
  - app_data:/var/lib/app     # nome dichiarato: app_data

# Se il progetto si chiama "single-node":
docker volume ls | grep single-node
# → single-node_app_data

Per rendere il nome indipendente dalla cartella, si usa il campo name: nella sezione volumes:

volumes:
  db-data:              # nome interno al compose (come una variabile)
    name: n8n_postgres_data   # nome fisico su Docker (stabile)

Questo e il pattern corretto per la produzione: il nome fisico non cambia mai, anche se si sposta la cartella del progetto.

External vs owned
#

Un volume puo essere di proprieta del compose o dichiarato come esterno:

# Compose CREA il volume (comportamento di default)
volumes:
  db-data:
    name: n8n_postgres_data

# Compose USA un volume gia esistente (non lo crea)
volumes:
  db-data:
    external: true
    name: n8n_postgres_data

external: true e utile quando il volume e stato creato manualmente o da un altro compose. Il rischio e che in una migrazione su un nuovo server il volume non esista e il compose fallisca all'avvio.

Collegato a
#

  • system — categoria
  • docker-volume — comandi: ls, inspect, migrazione, restore
  • docker-network-defense — reti Docker, stesso tipo di ragionamento su external/owned
  • docker-compose — dove i volumi vengono dichiarati e usati

Related