Volúmenes en Docker: bind mount vs volume
Bind mount o volume: la elección correcta en Docker depende del entorno, el sistema operativo y lo que necesitas persistir. Aquí van los criterios reales para decidir sin adivinar.
/var/lib/docker/volumes/ y son la opción recomendada en producción: más portables, con soporte para drivers externos (NFS, S3) y sin riesgo de permisos UID/GID. En Linux la diferencia de rendimiento es mínima; en Mac, los volúmenes evitan la capa de traducción del sistema de ficheros virtualizado.Funciona, pero ¿estás guardando los datos bien?
Tienes un docker-compose.yml que levanta sin errores, los servicios responden y todo parece en orden. Pero en algún momento te has preguntado: si borro este contenedor, ¿pierdo algo importante? ¿Ese volumen que declaré realmente persiste? ¿Debería estar usando un bind mount en lugar de esto?
Es una duda muy común cuando llevas un tiempo con Docker pero nadie te explicó la diferencia práctica entre las dos opciones. No es un fallo tuyo: la documentación oficial cubre los conceptos, pero no te dice cuándo elegir uno u otro según lo que estás montando en casa.
En este post vas a ver exactamente qué hace cada mecanismo por dentro, qué pasa con tus datos cuando paras o borras un contenedor, y cuándo tiene sentido usar bind mount, volumen con nombre o volumen anónimo en un homelab real. Sin dogmas, con ejemplos concretos.
Por qué importa
Desarrollo: bind mount
El código del host se refleja en tiempo real dentro del contenedor. Sin rebuild, sin copiar archivos.
Producción: volumen con nombre
Persiste independientemente del ciclo de vida del contenedor y soporta drivers remotos como NFS o S3.
Mac: rendimiento importa
Docker Desktop usa VirtioFS desde la versión 4.6 para reducir la penalización de I/O en bind mounts sobre el sistema de ficheros virtualizado.
Permisos: el talón de Aquiles
Un desajuste de UID/GID entre host y contenedor en bind mounts genera errores de escritura difíciles de diagnosticar.
Cómo funciona cada mecanismo por debajo
Antes de elegir, conviene tener claro qué hace cada uno. No son dos formas de hacer lo mismo: resuelven problemas distintos.
Bind mount
Un bind mount mapea una ruta concreta del host directamente dentro del contenedor. Si en tu máquina tienes /home/javi/proyectos/app, la puedes montar en /app dentro del contenedor. Cualquier cambio en el host se refleja al instante dentro, y viceversa. La ruta debe existir en el host antes de arrancar el contenedor.
docker run -v /home/javi/proyectos/app:/app mi-imagen
# o con --mount (equivalente en la mayoría de casos):
docker run --mount type=bind,source=/home/javi/proyectos/app,target=/app mi-imagen
Ambas sintaxis hacen lo mismo en la mayoría de casos, aunque --mount es más explícita y falla de forma más clara en edge cases como rutas inexistentes.
Docker volume
Un volumen lo gestiona el daemon de Docker. Le das un nombre, Docker lo crea y lo guarda en /var/lib/docker/volumes/ en Linux. No tienes que preocuparte de que la ruta del host exista ni de dónde está físicamente en el disco.
docker volume create mis-datos
docker run -v mis-datos:/app/data mi-imagen
Los volúmenes persisten aunque elimines el contenedor. Solo desaparecen si los borras explícitamente con docker volume rm o con docker volume prune, que elimina todos los que no están referenciados por ningún contenedor —operación irreversible, así que con cuidado.
Cuándo usar bind mount
El bind mount es el patrón estándar para desarrollo local. La razón es directa: el código que editas en tu editor se refleja al instante dentro del contenedor, sin rebuild.
Ejemplo típico: estás desarrollando una API con FastAPI o Express y quieres que cada guardado de fichero reinicie el servidor automáticamente. Con un bind mount que apunte a tu código fuente, el watcher del framework lo detecta igual que si corriera en bare metal. No hay paso intermedio.
- Desarrollo activo donde editas código y quieres ver cambios en caliente.
- Ficheros de configuración o seeds que viven en el repo y necesitas versionar.
- Logs o outputs del contenedor que quieres inspeccionar directamente en el host.
- Cuando controlas la ruta del host y no necesitas portabilidad entre máquinas distintas.
Hay casos donde hay que pensárselo: si la ruta del host no existe o cambia, el contenedor puede arrancar pero el mount falla de forma silenciosa o da un error confuso. Y en Mac y Windows, los bind mounts pasan por una capa de traducción del sistema de ficheros que puede penalizar el rendimiento en proyectos con mucho I/O —aunque Docker Desktop 4.6 introdujo VirtioFS para reducir bastante esa penalización.
Cuándo usar volume
Para datos que deben sobrevivir al ciclo de vida de varios contenedores, o para entornos donde no controlas la ruta del host, los volúmenes son la opción más robusta.
Un caso concreto: tienes un contenedor PostgreSQL que guarda la base de datos en /var/lib/postgresql/data. Si usas un bind mount, dependes de que esa ruta del host esté bien configurada en cada servidor donde despliegues. Con un volumen con nombre, Docker se encarga de la localización y puedes mover el stack a otra máquina con docker compose sin tocar rutas absolutas.
- Bases de datos: Postgres, MySQL, SQLite, Redis con persistencia activada.
- Datos de aplicación que deben sobrevivir a recreaciones o actualizaciones del contenedor.
- Entornos donde varios contenedores comparten el mismo almacenamiento.
- Cuando necesitas drivers externos: NFS, almacenamiento en red, plugins S3.
Un detalle importante sobre los volúmenes anónimos —los que no tienen nombre—: se eliminan cuando borras el contenedor con --rm o con docker rm -v. Son útiles para datos temporales durante una ejecución, pero son un antipatrón si lo que guardas ahí importa.
El problema de los permisos con bind mounts
Este es el punto donde más horas se pierden cuando se empieza con Docker. En un bind mount, los permisos UID/GID del host y del contenedor tienen que cuadrar. Si el proceso dentro del contenedor corre con UID 1000 pero el directorio del host pertenece a root (UID 0), vas a ver errores de escritura que a primera vista no tienen ninguna lógica.
# Diagnosticar: ver con qué usuario corre el proceso dentro del contenedor
docker exec mi-contenedor id
# Comparar con los permisos del directorio en el host
ls -la /ruta/del/host
Si los UIDs no coinciden, las opciones habituales son: cambiar el propietario del directorio en el host (chown), configurar el contenedor para que corra con el UID del host (--user), o ajustar la imagen para que el proceso use el UID correcto.
Con volúmenes gestionados por Docker este problema desaparece en la mayoría de casos. Docker inicializa los permisos del volumen en el primer arranque siguiendo las instrucciones de la imagen, así que el proceso del contenedor y los datos suelen estar alineados desde el principio.
Rendimiento: la respuesta depende del sistema operativo
Un malentendido frecuente es que los volúmenes son siempre más rápidos que los bind mounts. No es así de simple.
En Linux nativo: la diferencia de rendimiento entre bind mount y volume es mínima o directamente inexistente. Ambos acceden al sistema de ficheros del host con el mismo nivel de indirección. No hay razón técnica para elegir uno sobre el otro basándose solo en rendimiento.
En Mac y Windows: Docker Desktop virtualiza el sistema de ficheros, y los bind mounts sí pueden ser más lentos en proyectos con mucho I/O. Docker Desktop 4.6 introdujo VirtioFS como backend alternativo —y después lo activó por defecto— para reducir esa penalización. Si trabajas en Mac con proyectos grandes y notas lentitud, verifica que VirtioFS está activo en las preferencias de Docker Desktop antes de buscar otros culpables.
Dicho todo esto: persistencia no es lo mismo que backup. Ni los volúmenes ni los bind mounts hacen copias de seguridad automáticas. Si tienes datos importantes en un volumen, necesitas una estrategia de backup por separado —ya sea con docker run --rm -v mis-datos:/data -v $(pwd):/backup alpine tar czf /backup/datos.tar.gz /data o con cualquier otro método que se ajuste a tu setup.
Compose: donde los dos patrones conviven bien
Con Docker Compose la gestión de volúmenes se vuelve más legible y reproducible. Los volúmenes con nombre se declaran en la sección volumes: de nivel raíz y Docker los crea automáticamente en el primer up.
services:
db:
image: postgres:16
volumes:
- datos-db:/var/lib/postgresql/data
app:
image: mi-api
volumes:
- ./src:/app/src
volumes:
datos-db:
En este ejemplo conviven los dos patrones: bind mount para el código fuente (cambios en caliente durante desarrollo) y volume para los datos de Postgres (persistencia entre recreaciones del stack). Es una combinación habitual en entornos de desarrollo que luego pasan a producción con ajustes mínimos.
Para producción, lo habitual es eliminar los bind mounts del código fuente y dejar solo volúmenes para datos. El código entra en la imagen durante el build; no se monta desde el host. Así el artefacto es autónomo y no depende de rutas de la máquina donde corre.
Preguntas frecuentes
Q: ¿Cuándo usar bind mount en vez de volume?
A: Usa bind mount cuando necesitas que el código del host se refleje en tiempo real dentro del contenedor sin rebuild, el patrón estándar para desarrollo local. Para producción o datos que no tienen que estar en una ruta concreta del host, un volume con nombre es más predecible y portátil.
Q: ¿Qué pasa si borro el contenedor con --rm?
A: Depende del tipo de volumen: los volúmenes anónimos se eliminan junto al contenedor cuando usas --rm o docker rm -v, pero los volúmenes con nombre persisten hasta que ejecutas docker volume rm explícitamente. Si quieres conservar datos, dale siempre un nombre al volumen.
Q: ¿Son los volumes más rápidos que los bind mounts?
A: Depende del sistema operativo. En Mac y Windows, los volumes tienen mejor I/O que los bind mounts porque evitan la capa de traducción del sistema de ficheros virtualizado; Docker Desktop 4.6 mejoró esto con VirtioFS. En Linux nativo la diferencia es mínima o inexistente.
Q: ¿Por qué mi bind mount da errores de escritura?
A: Casi siempre es un desajuste de UID/GID entre el usuario del host y el usuario dentro del contenedor. Los permisos no se traducen automáticamente: si el proceso del contenedor corre como UID 1000 pero el directorio del host pertenece a root, obtendrás 'Permission denied'. Verifica con id en ambos lados y ajusta chown o el usuario en el Dockerfile.
Q: ¿Vale docker volume prune para limpiar espacio seguro?
A: Con precaución: docker volume prune elimina todos los volúmenes que no están referenciados por ningún contenedor en ese momento, y la operación es irreversible. Si tienes contenedores parados que sí usan esos volúmenes, también los borra. Revisa con docker volume ls y docker ps -a antes de ejecutarlo, y recuerda que 'persistencia' no es 'backup': no hay copia automática.











Deja una respuesta