Docker Volumes: qué son, tipos y ejemplos reales
Tiempo de lectura📚
20 minutos
Si llevas un tiempo usando Docker, seguro que en algún momento has hecho esto: levantas un contenedor con una base de datos, insertas datos, lo paras... y al volver a levantarlo los datos han desaparecido. Como si nunca hubieran existido.
Eso no es un bug. Es exactamente cómo está diseñado Docker. Y los volúmenes son la solución.
En este artículo te explico qué son los volúmenes, por qué existen, cuándo usar cada tipo y cómo se configuran. Con ejemplos reales, sin rodeos.
📚 Índice
- Que es un volumen en Docker
- El problema de los contenedores efimeros
- Tipos de almacenamiento en Docker
- Como usar Docker Volumes paso a paso
- Volumenes en Docker Compose
- Bind Mounts en desarrollo
- Compartir volumenes entre contenedores
- Como hacer backup de un volumen
- Errores frecuentes
- Cuando usar cada tipo
Que es un volumen en Docker
Un volumen es una carpeta que vive fuera del contenedor, gestionada por Docker, y que se monta dentro del contenedor en la ruta que tú elijas.
El contenedor puede leer y escribir en esa carpeta como si fuera suya. Pero cuando el contenedor se para, se elimina o se reemplaza por una nueva versión, los datos siguen ahí.
[Contenedor] [Contenedor]
└── /datos └── /datos ──┐
(se borra │ montado
al eliminar) [Volumen Docker]
└── datos seguros
En Linux, Docker guarda los volúmenes en /var/lib/docker/volumes/. Tú no tienes que preocuparte de esa ruta, Docker la gestiona por ti.
El problema de los contenedores efimeros
Cuando Docker arranca un contenedor, crea una capa de escritura temporal por encima de la imagen. Todo lo que el contenedor escribe (archivos, datos de base de datos, logs) va a esa capa. El problema es que esa capa desaparece cuando el contenedor se elimina.
Imagínalo así: es como escribir en una pizarra que se borra automáticamente al apagar la luz.
# Demostración del problema
docker run --name prueba ubuntu bash -c "echo 'hola mundo' > /tmp/archivo.txt"
docker start prueba
docker exec prueba cat /tmp/archivo.txt # funciona
docker rm prueba # eliminamos el contenedor
docker run --name prueba ubuntu bash -c "cat /tmp/archivo.txt"
# cat: /tmp/archivo.txt: No such file or directoryEl archivo se fue con el contenedor. Esto es un problema enorme para bases de datos, uploads de usuarios, logs que necesitas conservar, o cualquier dato que tenga que sobrevivir más allá de la vida del contenedor.
Aquí entran los volúmenes.
Tipos de almacenamiento en Docker
1.Volumes
Son el método recomendado. Docker los crea y gestiona en su propio directorio del sistema. Tú solo los referencias por nombre.
# Crear un volumen
docker volume create mis-datos
# Usarlo en un contenedor
docker run -v mis-datos:/app/datos mi-imagen¿Cuándo usarlos?
- Bases de datos
- Cualquier dato que necesite persistir en producción
- datos compartidos entre contenedores
2.Bind Mounts
Montan una carpeta real de tu máquina dentro del contenedor. Lo que pasa en esa carpeta se ve en tiempo real desde dentro y desde fuera del contenedor.
# Montar la carpeta actual dentro del contenedor
docker run -v $(pwd)/src:/app/src mi-imagen¿Cuándo usarlos?
- Desarrollo local
- Cuando quieras que el contenedor vea los cambios de tu código al momento sin hacer rebuild
3. tmpfs Mounts
Almacenan datos en la memoria RAM del host, no en disco. Son temporales por naturaleza: desaparecen cuando el contenedor se para.
docker run --tmpfs /tmp mi-imagen¿Cuándo usarlos?
- Datos sensibles que no deben tocarse en disco (tokens,secretos temporales)
- Cachés de alto rendimiento
Como usar Docker Volumes paso a paso
Crear y gestionar volúmenes
# Crear un volumen
docker volume create bbdd-datos
# Ver todos los volúmenes
docker volume ls
# Inspeccionar un volumen (dónde está, cuándo se creó...)
docker volume inspect bbdd-datos
# Eliminar un volumen (solo si ningún contenedor lo usa)
docker volume rm bbdd-datos
# Eliminar todos los volúmenes que no usa nadie
docker volume prune



Usar un volumen con un contenedor
# Con la sintaxis -v (más corta)
docker run -d \
--name mi-postgres \
-e POSTGRES_PASSWORD=secreto \
-v bbdd-datos:/var/lib/postgresql/data \
postgres:16
# Con la sintaxis --mount (más explícita, recomendada en producción)
docker run -d \
--name mi-postgres \
-e POSTGRES_PASSWORD=secreto \
--mount type=volume,source=bbdd-datos,target=/var/lib/postgresql/data \
postgres:16
Ambos comandos hacen lo mismo. La diferencia es que --mount es más verboso pero más claro: especifica el tipo, el nombre del volumen y la ruta de destino por separado.
Verificar que los datos persisten
# 1. Arrancamos PostgreSQL con volumen
docker run -d --name pg -e POSTGRES_PASSWORD=pass -v pg-datos:/var/lib/postgresql/data postgres:16

# 2. Creamos una base de datos
docker exec -it pg psql -U postgres -c "CREATE DATABASE miapp;"
# 3. Eliminamos el contenedor
docker rm -f pg
# 4. Creamos un contenedor nuevo con el mismo volumen
docker run -d --name pg-nuevo -e POSTGRES_PASSWORD=pass -v pg-datos:/var/lib/postgresql/data postgres:16

# 5. Comprobamos que la base de datos sigue ahí
docker exec -it pg-nuevo psql -U postgres -c "\l"
# miapp aparece en la lista. Los datos sobrevivieron.
Volumenes en Docker Compose
En proyectos reales casi siempre usarás Docker Compose. Aquí los volúmenes se definen en dos sitios: dentro del servicio (dónde se monta) y al final del archivo (declaración del volumen).
name: miapp
services:
api:
build: ./api
depends_on:
db:
condition: service_healthy
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: usuario
POSTGRES_PASSWORD: contraseña
POSTGRES_DB: mibase
volumes:
- pg_data:/var/lib/postgresql/data # <- aquí se monta
healthcheck:
test: ["CMD-SHELL", "pg_isready -U usuario -d mibase"]
interval: 10s
retries: 5
# Declaración de volúmenes al final del archivo
volumes:
pg_data: # Docker lo crea y gestiona automáticamenteCuando ejecutas docker compose up -d por primera vez, Docker crea el volumen pg_data automáticamente. Cuando haces docker compose down, el volumen no se elimina. Los datos sobreviven.
Para eliminarlo explícitamente
docker compose down -v # ⚠️ Esto borra los datosBind Mounts en desarrollo
Durante el desarrollo, lo más cómodo es usar un bind mount para que el contenedor vea los cambios de tu código al momento, sin tener que hacer rebuild de la imagen cada vez.
services:
api:
build: ./api
volumes:
- ./api:/app # bind mount: tu carpeta local -> contenedor
- /app/node_modules # excepción: node_modules viene de la imagen
command: node --watch index.js # recarga automática al cambiar archivosLo importante de este ejemplo:
- ./api:/app monta tu carpeta local dentro del contenedor. Cualquier cambio que hagas en tu editor aparece al momento dentro del contenedor.
- /app/node_modules es un volumen anónimo que "protege" la carpeta node_modules de ser sobreescrita por tu carpeta local. Si no haces esto, Docker montaría tu node_modules local (que puede tener diferencias de arquitectura) sobre el del contenedor.
Compartir volumenes entre contenedores
Varios contenedores pueden montar el mismo volumen al mismo tiempo. Esto es útil cuando, por ejemplo, un contenedor genera archivos y otro los sirve.
services:
generador:
image: mi-procesador
volumes:
- archivos-compartidos:/output
servidor:
image: nginx:alpine
volumes:
- archivos-compartidos:/usr/share/nginx/html:ro # :ro = solo lectura
ports:
- "80:80"
volumes:
archivos-compartidos:El generador escribe en /output. El servidor lee esos archivos y los sirve. El :ro al final del bind mount del servidor le impide escribir en el volumen, solo puede leer.
Como hacer backup de un volumen
Los volúmenes están en el sistema del host, pero no en una carpeta fácilmente accesible. La forma más limpia de hacer backup es con un contenedor temporal:
# Crear backup del volumen pg_data en un archivo .tar.gz
docker run --rm \
-v pg_data:/datos \
-v $(pwd):/backup \
ubuntu \
tar czf /backup/backup-pg.tar.gz -C /datos .
# Restaurar el backup en un volumen nuevo
docker volume create pg_data_restaurado
docker run --rm \
-v pg_data_restaurado:/datos \
-v $(pwd):/backup \
ubuntu \
tar xzf /backup/backup-pg.tar.gz -C /datosEste patrón usa un contenedor Ubuntu temporal que monta tanto el volumen como una carpeta de tu máquina, y ejecuta tar para comprimir o descomprimir. El --rm hace que el contenedor se elimine solo al terminar.
Errores frecuentes
"Eliminé el contenedor y perdí los datos"
Si usaste docker run sin -v, los datos estaban en la capa del contenedor. Solo los volúmenes persisten. Solución: siempre usa -v nombre:/ruta para cualquier dato que necesite sobrevivir.
"Hice docker compose down -v sin querer"
El flag -v elimina los volúmenes. Sin él, docker compose down conserva los datos. Si ya lo ejecutaste, los datos se han borrado y no hay forma de recuperarlos a menos que tengas un backup.
"El contenedor no arranca porque el volumen tiene datos de una versión anterior"
Esto pasa al actualizar la imagen de una base de datos. Solución: elimina el volumen y deja que la nueva versión lo inicialice desde cero, o sigue el proceso de migración que indica la imagen.
"No sé qué volúmenes están ocupando espacio"
docker system df # resumen general de espacio usado
docker volume ls # lista todos los volúmenes
docker volume prune # elimina los que no usa ningún contenedorCuando usar cada tipo
¿Cuándo usar cada tipo de almacenamiento?
| Situación | Tipo recomendado |
|---|---|
| Base de datos en producción | Volume nombrado |
| Código en desarrollo local | Bind mount |
| Datos sensibles temporales | tmpfs |
| Compartir archivos entre contenedores | Volume nombrado |
| Ver logs en tiempo real desde el host | Bind mount |
| Caché de alta velocidad | tmpfs |
Base de datos en producción
Volume nombradoCódigo en desarrollo local
Bind mountDatos sensibles temporales
tmpfsCompartir archivos entre contenedores
Volume nombradoVer logs en tiempo real desde el host
Bind mountCaché de alta velocidad
tmpfsLa regla general es: si el dato tiene que sobrevivir, usa un volume nombrado. Si necesitas que tu editor y el contenedor vean la misma carpeta, usa un bind mount.
Los volúmenes son uno de esos conceptos que, una vez los entiendes de verdad, te cambian la forma de construir aplicaciones con Docker. Dejan de ser ese "flag misterioso" del compose.yml y pasan a ser una herramienta que usas con intención.
Si quieres seguir profundizando en Docker Compose y ver cómo los volúmenes encajan en un proyecto real desde cero, tengo una guía completa en el blog donde construimos una aplicación paso a paso.