MySQL en Docker: cómo crear, conectar y conservar los datos
Para ejecutar MySQL en Docker sin perder la base de datos al sustituir el contenedor, hay que separar el servicio de sus datos: la imagen y el contenedor proporcionan el proceso, mientras que un volumen conserva los archivos de MySQL. Esa diferencia es esencial para desarrollar, probar o desplegar una aplicación con una configuración repetible.
Un contenedor puede eliminarse y crearse de nuevo; los datos guardados únicamente en su sistema de archivos desaparecen con él. La configuración siguiente usa Docker Compose, publica MySQL solo en el equipo local y monta un volumen persistente. Es un ejemplo para desarrollo: antes de usarlo en producción hay que revisar, entre otros aspectos, la seguridad, las copias de respaldo y la disponibilidad.
Crear el servicio con Docker Compose
Guarda este contenido en un archivo llamado compose.yaml:
services:
db:
image: mysql:8.4
restart: unless-stopped
environment:
MYSQL_DATABASE: aplicacion
MYSQL_USER: app
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
ports:
- "127.0.0.1:3306:3306"
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-uroot", "-p$${MYSQL_ROOT_PASSWORD}"]
interval: 5s
timeout: 3s
retries: 20
start_period: 20s
volumes:
mysql_data:
La etiqueta de imagen del ejemplo fija una versión mayor y menor concreta, en vez de usar latest. En un proyecto real, elige y prueba la versión que corresponda a tu aplicación; no actualices la imagen sin comprobar antes la compatibilidad y el procedimiento de migración. La etiqueta concreta tampoco sustituye una política de actualización: las imágenes deben revisarse y actualizarse de forma controlada.
En el mismo directorio, crea un archivo .env para que Compose sustituya las variables del ejemplo:
MYSQL_ROOT_PASSWORD=una_clave_larga_para_root
MYSQL_PASSWORD=otra_clave_larga_para_app
Usa valores distintos y evita guardar ese archivo en un repositorio compartido. Añádelo a .gitignore si el proyecto utiliza Git. Las variables de entorno facilitan la configuración, pero no son un almacén de secretos: quienes puedan inspeccionar el entorno del contenedor o la configuración de Docker podrían acceder a ellas. En entornos sensibles, utiliza un mecanismo de gestión de secretos adecuado.
Arranque y comprobación inicial
Desde el directorio del archivo, ejecuta docker compose up -d. Para consultar el estado, usa docker compose ps; para ver los mensajes de inicio, docker compose logs -f db. MySQL necesita un intervalo para inicializarse, así que el servicio puede estar creado antes de aceptar conexiones. El control de salud del ejemplo ayuda a distinguir ese estado de uno preparado, aunque una aplicación también debe gestionar reintentos de conexión.
La comprobación con mysqladmin ping indica si el servidor responde, no si todas las tablas de la aplicación existen ni si una operación concreta funcionará. Para comprobar una base de datos y un usuario, una prueba de conexión desde la aplicación o desde un cliente MySQL aporta una validación más completa.
Conectar desde el equipo o desde otro contenedor
El mapeo 127.0.0.1:3306:3306 permite conectar desde el propio equipo mediante 127.0.0.1 y el puerto 3306. Vincular el puerto a la dirección local reduce la exposición frente a publicarlo en todas las interfaces de red. Si el puerto ya está ocupado, cambia el de la izquierda, por ejemplo a 127.0.0.1:3307:3306; dentro del contenedor MySQL seguirá escuchando en el 3306.
Una aplicación que también se ejecuta en Compose no debería usar localhost para encontrar la base de datos: dentro de su contenedor, esa dirección apunta al propio contenedor de la aplicación. Debe conectarse al nombre del servicio, db, por el puerto interno 3306. Por ejemplo, una URL de conexión podría tener la forma mysql://app:CLAVE@db:3306/aplicacion; el formato exacto depende de la biblioteca utilizada y los caracteres especiales de la contraseña pueden requerir codificación.
En una aplicación con Compose, declara ambos servicios en la misma configuración y haz que la aplicación espere a que la base de datos esté disponible. La condición depends_on por sí sola ordena el arranque, pero no garantiza que MySQL ya acepte consultas, salvo que se configure una condición basada en el control de salud y la versión de Compose utilizada la admita. También es recomendable que la aplicación reintente conexiones ante fallos transitorios.
El volumen y el comportamiento de las variables de inicialización
mysql_data:/var/lib/mysql monta un volumen Docker en el directorio donde MySQL guarda sus archivos de datos. Al ejecutar docker compose down, el contenedor se elimina, pero el volumen nombrado permanece. Al volver a ejecutar docker compose up -d, el nuevo contenedor utiliza esos mismos datos.
En cambio, docker compose down -v elimina también los volúmenes declarados por Compose. Si se ejecuta en este ejemplo, se borrará el volumen de MySQL y se perderá la base almacenada allí. No lo uses como una limpieza rutinaria si necesitas conservar los datos. Un volumen tampoco es una copia de seguridad: si se corrompe o se elimina, no ofrece por sí solo una vía de recuperación.
Las variables MYSQL_DATABASE, MYSQL_USER y las contraseñas de inicialización sirven para preparar la instancia cuando el directorio de datos está vacío. Una vez inicializada la base dentro del volumen, cambiar esos valores en .env no modifica automáticamente el usuario, la contraseña ni las bases ya existentes. Si la contraseña cambia en el entorno pero no en MySQL, la aplicación intentará entrar con un valor distinto del registrado en el servidor.
Para cambiar credenciales en una instancia existente, modifica el usuario dentro de MySQL y actualiza después la configuración de la aplicación. No elimines el volumen para forzar la reinicialización salvo que quieras descartar todos sus datos. Si el contenedor no arranca tras un cambio de versión, revisa primero los registros y la compatibilidad de la actualización; evita probar soluciones destructivas sobre la única copia de los datos.
Errores frecuentes y cómo acotar el problema
- La aplicación recibe “connection refused”. Comprueba que el servicio está iniciado, consulta
docker compose logs dby confirma que la aplicación usa el nombre de servicio y el puerto interno correctos. Si conecta desde el host, verifica también el puerto publicado. - La autenticación falla. Revisa el nombre de usuario, la contraseña y la base solicitada. Si conservas un volumen previo, recuerda que cambiar las variables de entorno no actualiza las credenciales ya inicializadas.
- El puerto está ocupado. Cambia el puerto del host a la izquierda de los dos puntos, por ejemplo
3307:3306, y utiliza ese nuevo puerto al conectar desde el equipo. Entre contenedores se mantiene el 3306. - Los datos desaparecen al recrear el servicio. Confirma que el volumen está declarado y montado en
/var/lib/mysql. Comprueba también que no se ha ejecutadodown -vni se está usando un volumen distinto.
Copias de seguridad y uso más allá del desarrollo
Para una copia lógica, se puede exportar la base con herramientas como mysqldump y guardar el resultado fuera del volumen y del contenedor. La copia debe incluir las bases y los objetos que la aplicación necesite; después, prueba que se puede restaurar en una instancia separada. Un archivo generado pero nunca restaurado no demuestra por sí solo que el procedimiento de recuperación funcione.
En producción, limita quién puede acceder al puerto, protege las credenciales, define copias periódicas y una recuperación comprobada, y supervisa el espacio y el estado del servicio. Añade además un plan de actualización compatible con tu versión de MySQL y con el esquema de la aplicación. Para ampliar los conceptos de almacenamiento y gestión de información, consulta la sección de Bases de Datos.

