Contenido
Tutorial de MySQL en Docker: instala, conecta, haz copias de seguridad y restaura
Inicia con un solo comando
Primero, prepara la carpeta mysql-demo y coloca en initdb/ dos archivos SQL de inicialización (se explican más adelante):
mysql-demo/
└── initdb/
├── 01-schema.sql
└── 02-seed.sql
initdb/01-schema.sql:
CREATE TABLE products (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
price INT NOT NULL
);
initdb/02-seed.sql:
INSERT INTO products (name, price) VALUES ('珍珠奶茶', 65), ('鳳梨酥', 45), ('牛肉麵', 180);
Inicia MySQL desde la carpeta mysql-demo:
docker run -d --name mysql \
-e MYSQL_ROOT_PASSWORD=secret \
-e MYSQL_DATABASE=app \
-e MYSQL_USER=app \
-e MYSQL_PASSWORD=apppass \
-p 127.0.0.1:3306:3306 \
-v mysql_data:/var/lib/mysql \
-v "$(pwd)/initdb:/docker-entrypoint-initdb.d:ro" \
--restart unless-stopped \
mysql:8.4
docker run -d --name mysql `
-e MYSQL_ROOT_PASSWORD=secret `
-e MYSQL_DATABASE=app `
-e MYSQL_USER=app `
-e MYSQL_PASSWORD=apppass `
-p 127.0.0.1:3306:3306 `
-v mysql_data:/var/lib/mysql `
-v "${PWD}/initdb:/docker-entrypoint-initdb.d:ro" `
--restart unless-stopped `
mysql:8.4
docker run -d --name mysql ^
-e MYSQL_ROOT_PASSWORD=secret ^
-e MYSQL_DATABASE=app ^
-e MYSQL_USER=app ^
-e MYSQL_PASSWORD=apppass ^
-p 127.0.0.1:3306:3306 ^
-v mysql_data:/var/lib/mysql ^
-v "%cd%/initdb:/docker-entrypoint-initdb.d:ro" ^
--restart unless-stopped ^
mysql:8.4
$ docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=secret -e MYSQL_DATABASE=app -e MYSQL_USER=app -e MYSQL_PASSWORD=apppass -p 127.0.0.1:3306:3306 -v mysql_data:/var/lib/mysql -v "$(pwd)/initdb:/docker-entrypoint-initdb.d:ro" --restart unless-stopped mysql:8.4
Unable to find image 'mysql:8.4' locally
8.4: Pulling from library/mysql
e896da32f635: Pull complete
3527249a8025: Pull complete
6b54d15c954d: Pull complete
15ba404f5528: Pull complete
ad73434ffdad: Pull complete
bf11ec3e7e56: Pull complete
81077ec0e5ac: Pull complete
f51a9a1f0d20: Pull complete
4ba189089539: Pull complete
d4cc7515d7b7: Pull complete
Digest: sha256:24989179f58856db763f418010c1169fecc944e3ec1ea7e921572d83eb070f70
Status: Downloaded newer image for mysql:8.4
d92a7561ad58f958488c606bb07753aeffcc06639878a4c87ed676f6e1f99389
Comando equivalente para Windows (haz clic en las pestañas para cambiar):
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=secret -e MYSQL_DATABASE=app -e MYSQL_USER=app -e MYSQL_PASSWORD=apppass -p 127.0.0.1:3306:3306 -v mysql_data:/var/lib/mysql -v "$(pwd)/initdb:/docker-entrypoint-initdb.d:ro" --restart unless-stopped mysql:8.4
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=secret -e MYSQL_DATABASE=app -e MYSQL_USER=app -e MYSQL_PASSWORD=apppass -p 127.0.0.1:3306:3306 -v mysql_data:/var/lib/mysql -v "${PWD}/initdb:/docker-entrypoint-initdb.d:ro" --restart unless-stopped mysql:8.4
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=secret -e MYSQL_DATABASE=app -e MYSQL_USER=app -e MYSQL_PASSWORD=apppass -p 127.0.0.1:3306:3306 -v mysql_data:/var/lib/mysql -v "%cd%/initdb:/docker-entrypoint-initdb.d:ro" --restart unless-stopped mysql:8.4
| Parámetro | Descripción |
|---|---|
MYSQL_ROOT_PASSWORD | Contraseña de root (obligatoria; también puedes usar MYSQL_RANDOM_ROOT_PASSWORD=yes para generarla al azar) |
MYSQL_DATABASE | Base de datos que se crea durante el primer inicio |
MYSQL_USER / MYSQL_PASSWORD | Crea un usuario normal y le concede todos los permisos sobre MYSQL_DATABASE |
-p 127.0.0.1:3306:3306 | Solo permite conexiones locales; no se puede acceder desde fuera |
-v mysql_data:/var/lib/mysql | Los datos se guardan en el Volume llamado mysql_data |
-v .../initdb:/docker-entrypoint-initdb.d:ro | SQL que se ejecuta durante la inicialización |
Qué ocurre durante el primer inicio
$ docker logs mysql 2>&1 | grep -E "Entrypoint|initdb|ready for connections"
2026-10-13 00:46:59+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.4.11-1.el9 started.
2026-10-13 00:46:59+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
2026-10-13 00:46:59+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.4.11-1.el9 started.
2026-10-13 00:46:59+00:00 [Note] [Entrypoint]: Initializing database files
2026-10-13 00:47:02+00:00 [Note] [Entrypoint]: Database files initialized
2026-10-13 00:47:02+00:00 [Note] [Entrypoint]: Starting temporary server
2026-10-13T00:47:04.184489Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Socket: /var/run/mysqld/mysqlx.sock
2026-10-13T00:47:04.185012Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.4.11' socket: '/var/run/mysqld/mysqld.sock' port: 0 MySQL Community Server - GPL.
2026-10-13 00:47:04+00:00 [Note] [Entrypoint]: Temporary server started.
2026-10-13 00:47:06+00:00 [Note] [Entrypoint]: Creating database app
2026-10-13 00:47:06+00:00 [Note] [Entrypoint]: Creating user app
2026-10-13 00:47:06+00:00 [Note] [Entrypoint]: Giving user app access to schema app
2026-10-13 00:47:06+00:00 [Note] [Entrypoint]: /usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/01-schema.sql
2026-10-13 00:47:06+00:00 [Note] [Entrypoint]: /usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/02-seed.sql
2026-10-13 00:47:06+00:00 [Note] [Entrypoint]: Stopping temporary server
2026-10-13 00:47:07+00:00 [Note] [Entrypoint]: Temporary server stopped
2026-10-13 00:47:07+00:00 [Note] [Entrypoint]: MySQL init process done. Ready for start up.
2026-10-13T00:47:08.596982Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Bind-address: '::' port: 33060, socket: /var/run/mysqld/mysqlx.sock
2026-10-13T00:47:08.597330Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.4.11' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server - GPL.
En los registros se ve que, durante el primer inicio (cuando la carpeta está vacía), el script de inicio de la imagen realiza estos pasos en orden:
- Crea los archivos de la base de datos (
Initializing database files). - Inicia un servidor temporal (
Starting temporary server). Fíjate enport: 0: el servidor temporal no acepta conexiones de red; solo se puede conectar mediante un socket. - Crea la base de datos
app, el usuarioappy le concede permisos. - Ejecuta, en orden alfabético, los archivos
01-schema.sqly02-seed.sqlde/docker-entrypoint-initdb.d/. - Detiene el servidor temporal e inicia el servidor con la configuración definitiva. El
port: 3306de la última línea indica que ya acepta conexiones.
Todo el proceso tarda unos 10 segundos. /docker-entrypoint-initdb.d/ admite archivos .sql, .sql.gz y .sh, y solo se ejecutan durante la inicialización inicial. No vuelven a ejecutarse al reiniciar el contenedor.
Elegir una versión
| tag | Descripción |
|---|---|
mysql:8.4 | Versión de soporte a largo plazo (LTS), recomendada para producción |
mysql:9 | Versión de innovación, con funciones más recientes y un periodo de soporte más corto |
mariadb:11 | MariaDB, una variante muy compatible con MySQL; las variables de entorno usan el prefijo MARIADB_ |
Conexión
Desde el contenedor
El cliente mysql ya está disponible en el contenedor. Para una sesión interactiva, ejecuta docker exec -it mysql mysql -uapp -p e introduce la contraseña para acceder al indicador mysql>. También puedes ejecutar SQL directamente con -e:
$ docker exec mysql mysql -uapp -papppass app -t -e "SELECT * FROM products;"
mysql: [Warning] Using a password on the command line interface can be insecure.
+----+--------------+-------+
| id | name | price |
+----+--------------+-------+
| 1 | 珍珠奶茶 | 65 |
| 2 | 鳳梨酥 | 45 |
| 3 | 牛肉麵 | 180 |
+----+--------------+-------+
$ docker exec mysql mysql -uroot -psecret -t -e "SELECT VERSION(), @@character_set_server, @@collation_server;"
mysql: [Warning] Using a password on the command line interface can be insecure.
+-----------+------------------------+--------------------+
| VERSION() | @@character_set_server | @@collation_server |
+-----------+------------------------+--------------------+
| 8.4.11 | utf8mb4 | utf8mb4_0900_ai_ci |
+-----------+------------------------+--------------------+
-tmuestra la salida en formato de tabla (en modo no interactivo, el formato predeterminado usa tabuladores).- Los datos en chino se almacenan y recuperan correctamente. El juego de caracteres del servidor es
utf8mb4y la intercalación esutf8mb4_0900_ai_ci, los valores predeterminados de MySQL 8; no hace falta configurar nada más. Los bordes de las filas con caracteres chinos no quedan alineados porque el clientemysqlcalcula así el ancho de los caracteres chinos. Los datos no tienen ningún problema. - El mensaje
[Warning] Using a password on the command line interface can be insecure., que aparece cada vez, advierte que otras personas podrían ver la contraseña escrita en el comando en la lista de procesos. Puedes ignorarlo en este ejemplo.
Desde el host: diferencia entre localhost y 127.0.0.1
En el host, conecta cualquier cliente MySQL (MySQL Workbench, DBeaver o el comando mysql) a 127.0.0.1:3306. Escribe 127.0.0.1 en lugar de localhost. Compruébalo con un contenedor que usa --network host para simular el cliente mysql del host:
$ docker run --rm --network host mysql:8.4 mysql -h localhost -uapp -papppass -e "SELECT 1"
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)
$ docker run --rm --network host mysql:8.4 mysql -h 127.0.0.1 -uapp -papppass -e "SELECT USER(), CURRENT_USER()"
mysql: [Warning] Using a password on the command line interface can be insecure.
USER() CURRENT_USER()
app@172.18.0.1 app@%
-h localhost: falla conCan't connect to local MySQL server through socket. Cuando el cliente MySQL velocalhost, usa un archivo de socket Unix en lugar de TCP, pero ese archivo de socket no existe en el host (está dentro del contenedor).-h 127.0.0.1: funciona; se conecta por TCP a través del puerto asignado con-p.USER()muestraapp@172.18.0.1: desde la perspectiva de MySQL, la conexión viene de la IP de la puerta de enlace de la red Docker, no de127.0.0.1.app@%enCURRENT_USER()indica que esta cuenta permite conexiones desde cualquier host.
Desde otros contenedores
Coloca MySQL y la aplicación en la misma red personalizada para conectarte usando el nombre del contenedor (la red bridge predeterminada no permite resolver nombres; consulta Conexión entre redes y contenedores):
$ docker network create appnet
595dfaad48e7460c5996b90a62402e50e31fb790a1f04b3cdc67ac12be482db6
$ docker network connect appnet mysql
$ docker run --rm --network appnet mysql:8.4 mysql -h mysql -uapp -papppass app -e "SELECT COUNT(*) FROM products"
mysql: [Warning] Using a password on the command line interface can be insecure.
COUNT(*)
3
La cadena de conexión de la aplicación se escribe así:
mysql://app:apppass@mysql:3306/app
El nombre de host es el nombre del contenedor mysql (en Compose, el nombre del servicio) y el puerto es 3306, dentro del contenedor. No hace falta -p para conectar contenedores entre sí.
Gestiona con Compose
En proyectos reales, se recomienda usar Compose. Añade compose.yaml, .env y el archivo de configuración personalizado my.cnf a mysql-demo:
mysql-demo/
├── compose.yaml
├── .env
├── my.cnf
└── initdb/
├── 01-schema.sql
└── 02-seed.sql
.env:
MYSQL_ROOT_PASSWORD=change-this-root-password
MYSQL_PASSWORD=change-this-app-password
my.cnf:
[mysqld]
max_connections = 300
innodb_buffer_pool_size = 256M
compose.yaml:
services:
db:
image: mysql:8.4
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: app
MYSQL_USER: app
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
TZ: Asia/Taipei
ports:
- "127.0.0.1:3306:3306"
volumes:
- db_data:/var/lib/mysql
- ./initdb:/docker-entrypoint-initdb.d:ro
- ./my.cnf:/etc/mysql/conf.d/custom.cnf:ro
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -uroot -p\"$$MYSQL_ROOT_PASSWORD\" --silent"]
interval: 10s
retries: 10
start_period: 30s
restart: unless-stopped
volumes:
db_data:
| Configuración | Descripción |
|---|---|
TZ: Asia/Taipei | Configura la zona horaria del contenedor; MySQL usará esta zona horaria para NOW() |
./my.cnf:/etc/mysql/conf.d/custom.cnf | MySQL leerá los archivos .cnf de /etc/mysql/conf.d/ |
$$MYSQL_ROOT_PASSWORD | $$ hace que Compose no sustituya la variable y la deja para que el shell del contenedor lea la variable de entorno del contenedor |
-h 127.0.0.1 | Importante, consulta la explicación de abajo |
$ docker compose up -d --wait
[+] up 3/3
✔ Network mysql-demo_default Created 0.0s
✔ Volume mysql-demo_db_data Created 0.0s
✔ Container mysql-demo-db-1 Healthy 10.9s
$ docker compose exec db sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e "SELECT 1"'
mysql: [Warning] Using a password on the command line interface can be insecure.
1
1
$ docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
mysql-demo-db-1 mysql:8.4 "docker-entrypoint.s…" db 12 seconds ago Up 11 seconds (healthy) 127.0.0.1:3306->3306/tcp, 33060/tcp
$ docker compose exec db sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -t -e "SELECT @@max_connections, @@innodb_buffer_pool_size DIV 1048576 AS buffer_pool_mb, @@system_time_zone, NOW();"'
mysql: [Warning] Using a password on the command line interface can be insecure.
+-------------------+----------------+--------------------+---------------------+
| @@max_connections | buffer_pool_mb | @@system_time_zone | NOW() |
+-------------------+----------------+--------------------+---------------------+
| 300 | 256 | CST | 2026-10-13 08:49:41 |
+-------------------+----------------+--------------------+---------------------+
$ docker compose exec db sh -c 'mysql -uapp -p"$MYSQL_PASSWORD" app -t -e "SELECT * FROM products"'
mysql: [Warning] Using a password on the command line interface can be insecure.
+----+--------------+-------+
| id | name | price |
+----+--------------+-------+
| 1 | 珍珠奶茶 | 65 |
| 2 | 鳳梨酥 | 45 |
| 3 | 牛肉麵 | 180 |
+----+--------------+-------+
--waitespera hasta que el servicio estéhealthyantes de regresar.Healthytardó unos 11 segundos, un tiempo similar al de la inicialización que vimos antes.max_connectionses 300 ybuffer_pool_mbes 256:my.cnfse aplicó correctamente.@@system_time_zoneesCST(hora estándar de China, UTC+8) yNOW()muestra la hora de Taipéi.- El SQL de inicialización también se ejecutó:
productscontiene tres filas.
Trampa de la comprobación de estado: localhost indica healthy demasiado pronto
Una forma habitual de comprobar el estado de MySQL en internet es mysqladmin ping -h localhost. En una prueba, cambiamos el 127.0.0.1 de arriba por localhost:
$ docker compose up -d --wait
[+] up 3/3
✔ Network mysql-demo_default Created 0.0s
✔ Volume mysql-demo_db_data Created 0.0s
✔ Container mysql-demo-db-1 Healthy 5.8s
$ docker compose exec db sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e "SELECT 1"'
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
$ docker compose logs db 2>&1 | grep -E "Temporary server|init process done|port: 3306"
db-1 | 2026-10-13 08:49:02+08:00 [Note] [Entrypoint]: Temporary server started.
$ docker compose exec db sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e "SELECT 1"'
mysql: [Warning] Using a password on the command line interface can be insecure.
1
1
- Solo habían pasado 5.8 segundos cuando indicó
Healthy, pero justo después falló la conexión con root. El registro muestra que todavía estaba enTemporary server started. - El intento se completó correctamente al repetirlo 10 segundos después.
La causa es que, durante la inicialización, el servidor temporal solo acepta conexiones mediante socket, y -h localhost usa precisamente el socket. Por eso ping obtuvo una respuesta. Además, mysqladmin ping considera que la comprobación se ha completado con éxito con solo recibir una respuesta del servidor, aunque todavía no sea posible iniciar sesión con el usuario y la contraseña. Como resultado, Compose cree que la base de datos ya está lista e inicia los servicios que dependen de ella (depends_on con condition: service_healthy); la primera conexión de la aplicación puede fallar.
Al cambiarlo por -h 127.0.0.1, se usa TCP. El servidor temporal no acepta conexiones de red (port: 0), así que la comprobación solo indicará healthy cuando se inicie el servidor normal.
Copia de seguridad y restauración
Exporta el SQL con mysqldump, luego elimina deliberadamente la tabla y restáurala desde la copia de seguridad:
$ docker exec mysql sh -c 'mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --single-transaction app' > app.sql
mysqldump: [Warning] Using a password on the command line interface can be insecure.
$ ls -lh app.sql && grep -c "INSERT INTO" app.sql
-rw-r--r-- 1 root root 2.0K Oct 13 00:47 app.sql
1
$ docker exec mysql mysql -uroot -psecret -e "DROP TABLE app.products"
mysql: [Warning] Using a password on the command line interface can be insecure.
$ docker exec mysql mysql -uapp -papppass app -e "SELECT COUNT(*) FROM products"
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 1146 (42S02) at line 1: Table 'app.products' doesn't exist
$ docker exec -i mysql sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" app' < app.sql
mysql: [Warning] Using a password on the command line interface can be insecure.
$ docker exec mysql mysql -uapp -papppass app -t -e "SELECT * FROM products"
mysql: [Warning] Using a password on the command line interface can be insecure.
+----+--------------+-------+
| id | name | price |
+----+--------------+-------+
| 1 | 珍珠奶茶 | 65 |
| 2 | 鳳梨酥 | 45 |
| 3 | 牛肉麵 | 180 |
+----+--------------+-------+
- El comando de copia de seguridad se envuelve en
sh -c '...';$MYSQL_ROOT_PASSWORDse expande dentro del contenedor, por lo que la contraseña no aparece en el historial de comandos del host. Las comillas simples externas evitan que el shell del host sustituya primero la variable. --single-transactionexporta las tablas InnoDB en una instantánea coherente sin bloquearlas durante la copia de seguridad. Añádelo siempre en producción.- El archivo de copia de seguridad
app.sqlocupa 2.0K e incluye 1 comandoINSERT INTO(las tres filas se escriben en el mismo INSERT). - Después de
DROP TABLE, la consulta devuelveTable 'app.products' doesn't exist. - Para restaurar, usa
docker exec -iy pasaapp.sqldesde el host amysqldentro del contenedor mediante la entrada estándar. Los datos se restauran por completo.
Al restaurar, usa solo -i, no -it. Si la entrada estándar es un archivo y no un terminal, -t produce un error directamente:
$ docker exec -it mysql sh -c "echo hi" < /dev/null
cannot attach stdin to a TTY-enabled container because stdin is not a terminal
Otros comandos habituales de copia de seguridad:
# Copia de seguridad de todas las bases de datos (incluidos usuarios y permisos)
docker exec mysql sh -c 'mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --all-databases --single-transaction' > all.sql
# Crear y comprimir una copia de seguridad
docker exec mysql sh -c 'mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --single-transaction app' | gzip > app.sql.gz
# Restaurar una copia de seguridad comprimida
gunzip < app.sql.gz | docker exec -i mysql sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" app'
Copia de seguridad y restauración para usuarios de Windows
PowerShell no admite <, y Windows PowerShell 5.1 guarda los archivos redirigidos con > en codificación UTF-16, lo que provoca errores al importarlos. Usa un archivo creado dentro del contenedor y luego cópialo con docker cp. Este método funciona en todos los sistemas:
# Copia de seguridad: exporta a /tmp/app.sql dentro del contenedor y cópialo fuera
docker exec mysql sh -c 'mysqldump -uroot -p$MYSQL_ROOT_PASSWORD --single-transaction --result-file=/tmp/app.sql app'
docker cp mysql:/tmp/app.sql app.sql
# Restauración: primero cópialo dentro y luego impórtalo en el contenedor
docker cp app.sql mysql:/tmp/restore.sql
docker exec mysql sh -c 'mysql -uroot -p$MYSQL_ROOT_PASSWORD app < /tmp/restore.sql'
# Copia de seguridad: exporta a /tmp/app.sql dentro del contenedor y cópialo fuera
docker exec mysql sh -c 'mysqldump -uroot -p$MYSQL_ROOT_PASSWORD --single-transaction --result-file=/tmp/app.sql app'
docker cp mysql:/tmp/app.sql app.sql
# Restauración: primero cópialo dentro y luego impórtalo en el contenedor
docker cp app.sql mysql:/tmp/restore.sql
docker exec mysql sh -c 'mysql -uroot -p$MYSQL_ROOT_PASSWORD app < /tmp/restore.sql'
REM Copia de seguridad: exporta a /tmp/app.sql dentro del contenedor y cópialo fuera
docker exec mysql sh -c "mysqldump -uroot -p$MYSQL_ROOT_PASSWORD --single-transaction --result-file=/tmp/app.sql app"
docker cp mysql:/tmp/app.sql app.sql
REM Restauración: primero cópialo dentro y luego impórtalo en el contenedor
docker cp app.sql mysql:/tmp/restore.sql
docker exec mysql sh -c "mysql -uroot -p$MYSQL_ROOT_PASSWORD app < /tmp/restore.sql"
--result-filehace que mysqldump escriba directamente en un archivo, sin pasar por la redirección de la terminal.- El
<del comando está dentro de la cadena desh -cy lo procesa el shell dentro del contenedor, así que también funciona en PowerShell. - No pongas comillas en
-p$MYSQL_ROOT_PASSWORDpara evitar problemas con el manejo de comillas anidadas en PowerShell 5.1 (la contraseña no debe contener espacios).
Estos comandos se probaron con Docker 29 y restauraron correctamente las 3 filas:
$ docker exec mysql sh -c 'mysql -uroot -p$MYSQL_ROOT_PASSWORD app -e "CREATE TABLE t (id INT); INSERT INTO t VALUES (1),(2),(3);"'
mysql: [Warning] Using a password on the command line interface can be insecure.
$ docker exec mysql sh -c 'mysqldump -uroot -p$MYSQL_ROOT_PASSWORD --single-transaction --result-file=/tmp/app.sql app'
mysqldump: [Warning] Using a password on the command line interface can be insecure.
$ docker cp mysql:/tmp/app.sql app.sql
$ ls -l app.sql
-rw-rw-rw- 1 root root 1788 Oct 14 09:48 app.sql
$ docker exec mysql sh -c 'mysql -uroot -p$MYSQL_ROOT_PASSWORD app -e "DROP TABLE t"'
mysql: [Warning] Using a password on the command line interface can be insecure.
$ docker cp app.sql mysql:/tmp/restore.sql
$ docker exec mysql sh -c 'mysql -uroot -p$MYSQL_ROOT_PASSWORD app < /tmp/restore.sql'
mysql: [Warning] Using a password on the command line interface can be insecure.
$ docker exec mysql sh -c 'mysql -uroot -p$MYSQL_ROOT_PASSWORD app -e "SELECT COUNT(*) FROM t"'
mysql: [Warning] Using a password on the command line interface can be insecure.
COUNT(*)
3
Prueba práctica: cambiar la variable de contraseña no surte efecto
Variables como MYSQL_ROOT_PASSWORD se usan solo durante la primera inicialización (cuando el volumen está vacío). Elimina el contenedor, conserva el volumen y vuelve a crear el contenedor con una contraseña diferente:
$ docker rm -f mysql
mysql
$ docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=different -v mysql_data:/var/lib/mysql mysql:8.4
2a0f52d379b574033ae1571ab6873f37e8ab79ccd3830f6feac629696b74ae42
$ docker logs mysql 2>&1 | grep -c "Initializing database"
0
$ docker exec mysql mysql -uroot -pdifferent -e "SELECT 1"
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
$ docker exec mysql mysql -uroot -psecret -e "SELECT 1"
mysql: [Warning] Using a password on the command line interface can be insecure.
1
1
Initializing databaseaparece 0 veces en los registros: el volumen ya contiene datos y el script de inicio omitió la inicialización.- No puedes iniciar sesión con la nueva contraseña
different; la contraseña anteriorsecretsigue funcionando.
Esto significa que, una vez que hay datos, cambiar MYSQL_ROOT_PASSWORD no tiene ningún efecto. Para cambiar la contraseña, inicia sesión con la contraseña anterior y ejecuta SQL:
ALTER USER 'root'@'%' IDENTIFIED BY 'nueva contraseña';
ALTER USER 'root'@'localhost' IDENTIFIED BY 'nueva contraseña';
O, si no necesitas conservar los datos, elimina el volumen y vuelve a inicializar (docker compose down -v; se perderán todos los datos).
Solución de problemas
Access denied for user 'root'@'...'
$ docker exec mysql mysql -uroot -pwrong -e "SELECT 1"
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
- La contraseña es incorrecta o el volumen se inicializó con una contraseña anterior (consulta la prueba práctica anterior).
- De forma predeterminada, root tiene dos cuentas:
root@%yroot@localhost, que permiten conexiones desde cualquier host. Si creas un usuario, comprueba que su host sea%; de lo contrario, solo podrá conectarse desde un host específico.
Can't connect to local MySQL server through socket
El cliente interpreta localhost como una conexión mediante socket. Usa 127.0.0.1 (consulta la prueba práctica anterior).
El contenedor se cierra justo después de iniciarse
Consulta los registros: docker logs mysql. Causas habituales:
- No se ha configurado
MYSQL_ROOT_PASSWORD(niMYSQL_ALLOW_EMPTY_PASSWORDniMYSQL_RANDOM_ROOT_PASSWORD). - El volumen contiene datos inicializados por otra versión (por ejemplo, 5.7 o MariaDB), que son incompatibles con la versión actual.
- El host no tiene suficiente memoria.
Actualizar desde 5.7 / 8.0
No montes directamente un volumen antiguo en una imagen nueva. Se recomienda exportar primero con mysqldump, importar los datos a una base de datos vacía de la nueva versión y validar primero en un entorno de pruebas.
Resumen
- Coloca los scripts SQL de inicialización en
/docker-entrypoint-initdb.d/; se ejecutan solo durante la primera inicialización. - Para conectarte desde el host, escribe
127.0.0.1;localhostusará un socket. - Configura la comprobación de estado con
mysqladmin ping -h 127.0.0.1. - Haz copias de seguridad con
mysqldump --single-transactiony prueba la restauración.
Ejercicios
Ejercicio 1: Usa un solo comando para listar todas las bases de datos del contenedor MySQL.
Respuesta
docker exec mysql sh -c 'mysql -uroot -p$MYSQL_ROOT_PASSWORD -e "SHOW DATABASES"'
Ejercicio 2: La configuración de MySQL en Compose tiene ports: "127.0.0.1:3306:3306". ¿Qué nombre de host y puerto debe usar una aplicación del mismo proyecto Compose para conectarse?
Respuesta
El nombre de host es el nombre del servicio db y el puerto es 3306, el del contenedor. ports solo afecta a las conexiones desde el host; los contenedores no lo necesitan para comunicarse entre sí.
Preguntas frecuentes
¿El texto chino aparece como caracteres ilegibles?
MySQL 8 usa utf8mb4 de forma predeterminada. Los caracteres ilegibles suelen deberse al juego de caracteres de la conexión del cliente o a la codificación del archivo importado; las copias guardadas con > en Windows PowerShell 5.1 usan UTF-16. Usa el método docker cp de este capítulo.
¿Debo usar MySQL o MariaDB?
Ambos son muy compatibles. Programas comunes como WordPress admiten los dos; las variables de entorno de MariaDB empiezan por MARIADB_.
¿No se aplica el cambio de MYSQL_ROOT_PASSWORD?
La variable de contraseña solo se usa cuando el Volume está vacío. Después, debes cambiarla con SQL; consulta las pruebas de este capítulo.
Actualizado 14 de octubre de 2026