Cuando perdí datos de producción por no usar volúmenes (y cómo no repetirlo)
Era viernes por la tarde. Reinicié un contenedor de PostgreSQL que habíamos levantado «temporalmente» hacía tres meses. En segundos entendí el error: los datos vivían dentro del contenedor, no en un volumen. Tres meses de datos del cliente, gone. Esa tarde aprendí para siempre qué es la persistencia en Docker.
El problema: los contenedores son efímeros por diseño
Cuando Docker crea un contenedor, agrega una capa de escritura sobre la imagen base. Todo lo que el proceso escribe va ahí. Cuando el contenedor se destruye, esa capa desaparece con él. Es intencional: los contenedores son desechables. El problema surge cuando guardamos datos importantes en esa capa temporal.
# Demostración del problema:
docker run -d --name mi-postgres postgres:16
docker exec -it mi-postgres psql -U postgres -c "CREATE TABLE clientes (id serial, nombre text);"
docker exec -it mi-postgres psql -U postgres -c "INSERT INTO clientes VALUES (1, 'Empresa ABC');"
# Destruir el contenedor...
docker rm -f mi-postgres
# Volver a crearlo...
docker run -d --name mi-postgres postgres:16
docker exec -it mi-postgres psql -U postgres -c "SELECT * FROM clientes;"
# ERROR: relation "clientes" does not exist
# Los datos desaparecieron.
Las tres formas de manejar datos en Docker
| Tipo | Dónde vive | Gestionado por | Caso de uso |
|---|---|---|---|
| Volumes | /var/lib/docker/volumes/ | Docker | Bases de datos, datos persistentes en producción |
| Bind Mounts | Cualquier path del host | Vos | Desarrollo local, compartir código |
| tmpfs mounts | RAM del host | Docker | Datos sensibles temporales, caché efímera |
Volúmenes nombrados: la forma correcta para producción
Los volúmenes son el mecanismo preferido para datos persistentes. Docker los gestiona completamente: sabe dónde están, los protege de eliminaciones accidentales y los puede mover entre contenedores fácilmente.
# Crear un volumen nombrado
docker volume create postgres-data
# Usarlo con PostgreSQL
docker run -d --name mi-postgres -e POSTGRES_PASSWORD=secreto -e POSTGRES_DB=miapp -v postgres-data:/var/lib/postgresql/data -p 5432:5432 --restart=unless-stopped postgres:16-alpine
# Ahora los datos sobreviven al contenedor:
docker rm -f mi-postgres
docker run -d --name mi-postgres -e POSTGRES_PASSWORD=secreto -v postgres-data:/var/lib/postgresql/data postgres:16-alpine
# ✅ Los datos siguen ahí
Comandos de gestión de volúmenes
docker volume ls # listar volúmenes
docker volume inspect postgres-data # detalle de un volumen
docker volume rm postgres-data # eliminar (solo si no está en uso)
docker volume prune # eliminar todos los no usados
Bind Mounts: para desarrollo local
Los bind mounts montan un directorio del host directamente en el contenedor. Los uso mucho en desarrollo: edito el código en mi máquina y el contenedor lo ve en tiempo real, sin necesidad de hacer rebuild.
# Desarrollo de API .NET con hot reload
docker run -d --name mi-api-dev -v $(pwd):/app -w /app -p 5000:80 -e ASPNETCORE_ENVIRONMENT=Development mcr.microsoft.com/dotnet/sdk:8.0 dotnet watch run
# Desarrollo Node.js con nodemon
docker run -d --name mi-node-dev -v $(pwd):/app -w /app -p 3000:3000 node:20-alpine sh -c "npm install && npm run dev"
Backup y restauración de volúmenes
Después del incidente del viernes, implementé backups automáticos de todos los volúmenes de producción. La estrategia más confiable que encontré:
# Backup de un volumen a un archivo tar
docker run --rm -v postgres-data:/data -v $(pwd)/backups:/backups alpine tar czf /backups/postgres-$(date +%Y%m%d_%H%M%S).tar.gz -C /data .
# Restaurar desde backup
docker run --rm -v postgres-data:/data -v $(pwd)/backups:/backups alpine tar xzf /backups/postgres-20260311_030000.tar.gz -C /data
# Backup directo de PostgreSQL (más limpio para DBs)
docker exec mi-postgres pg_dump -U postgres miapp > backup_$(date +%Y%m%d).sql
La regla que aplico siempre
Si el dato importa, va en un volumen. Sin excepción. Bases de datos, archivos subidos por usuarios, certificados, configuraciones que cambian en runtime. Todo lo que no quiero perder cuando hago docker rm -f va en un volumen nombrado. El viernes que perdí esos datos fue la última vez que cometí ese error.
← Artículo anterior: Ciclo de vida de un contenedor | Serie Docker Completo | Próximo: Redes en Docker →
← Artículo anterior: docker run y todo lo que nadie te explica del ciclo de vida de un contenedor | Serie Docker Completo | Próximo: Redes en Docker: de ‘no puedo conectar mis contenedores’ a entenderlo de verdad →
