Conteúdo

MySQL com Docker: tutorial para instalar, conectar, fazer backup e restaurar

Inicie com um único comando

Primeiro, prepare uma pasta mysql-demo e coloque nela dois arquivos SQL de inicialização (explicados adiante) dentro de initdb/:

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);

Inicie o MySQL na pasta 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

Forma correspondente para Windows (selecione uma aba para alternar):

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âmetroDescrição
MYSQL_ROOT_PASSWORDSenha do root (obrigatória; como alternativa, use MYSQL_RANDOM_ROOT_PASSWORD=yes para gerar uma senha aleatória)
MYSQL_DATABASEBanco de dados criado na primeira inicialização
MYSQL_USER / MYSQL_PASSWORDCria um usuário comum e concede a ele todas as permissões em MYSQL_DATABASE
-p 127.0.0.1:3306:3306Disponibiliza apenas localmente; não é acessível de fora
-v mysql_data:/var/lib/mysqlOs dados ficam no Volume chamado mysql_data
-v .../initdb:/docker-entrypoint-initdb.d:roSQL executado durante a inicialização

O que acontece na primeira inicialização

$ 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.

Pelos logs, o script de inicialização da imagem, na primeira inicialização (quando a pasta de dados está vazia), executa estas etapas:

  1. Cria os arquivos do banco de dados (Initializing database files).
  2. Inicia um servidor temporário (Starting temporary server). Observe port: 0: o servidor temporário não aceita conexões de rede; só é possível conectar por socket.
  3. Cria o banco de dados app, o usuário app e concede as permissões.
  4. Executa os arquivos 01-schema.sql e 02-seed.sql de /docker-entrypoint-initdb.d/, em ordem alfabética.
  5. Para o servidor temporário e inicia o servidor com a configuração final. Só o port: 3306 da última linha indica que já é possível conectar.

Todo o processo leva cerca de 10 segundos. /docker-entrypoint-initdb.d/ aceita arquivos .sql, .sql.gz e .sh, e só é executado na inicialização inicial. Reiniciar o contêiner depois disso não executa os arquivos novamente.

Escolha da versão

tagDescrição
mysql:8.4Versão com suporte de longo prazo (LTS), recomendada para produção
mysql:9Versão de inovação, com recursos mais novos e período de suporte mais curto
mariadb:11MariaDB, uma ramificação altamente compatível com MySQL; as variáveis de ambiente passam a começar com MARIADB_

Conexão

De dentro do contêiner

O cliente mysql já está dentro do contêiner. Para uma sessão interativa, execute docker exec -it mysql mysql -uapp -p, digite a senha e acesse o prompt mysql>. Você também pode executar SQL diretamente com -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 |
+-----------+------------------------+--------------------+
  • -t exibe a saída em formato de tabela (sem modo interativo, o padrão é separar por tabulações).
  • Os dados em chinês são armazenados e lidos corretamente. O conjunto de caracteres do servidor é utf8mb4 e a regra de ordenação é utf8mb4_0900_ai_ci, valores padrão do MySQL 8 que não precisam de configuração adicional. As bordas das linhas com caracteres chineses não ficam alinhadas porque o cliente mysql calcula a largura desses caracteres dessa forma; os dados não têm problema.
  • O aviso [Warning] Using a password on the command line interface can be insecure. que aparece sempre lembra que outras pessoas podem ver a senha escrita no comando na lista de processos. Você pode ignorá-lo durante a demonstração.

Do host: diferença entre localhost e 127.0.0.1

No host, use qualquer cliente MySQL (MySQL Workbench, DBeaver ou o comando mysql) para conectar a 127.0.0.1:3306. Especifique 127.0.0.1 em vez de localhost. Use um contêiner com --network host para simular o cliente mysql no host e testar:

$ 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: falha com Can't connect to local MySQL server through socket. Quando o cliente MySQL recebe localhost, ele usa um arquivo de socket Unix em vez de TCP. Esse arquivo de socket não existe no host (ele está dentro do contêiner).
  • -h 127.0.0.1: funciona, usando TCP pela porta mapeada com -p.
  • USER() mostra app@172.18.0.1: do ponto de vista do MySQL, a conexão vem do IP do gateway da rede Docker, não de 127.0.0.1. app@% em CURRENT_USER() indica que essa conta pode se conectar de qualquer host.

De outros contêineres

Coloque o MySQL e o aplicativo na mesma rede personalizada para conectar pelo nome do contêiner (a rede bridge padrão não resolve nomes. Consulte Conexão entre redes e contêineres):

$ 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

A string de conexão do aplicativo fica assim:

mysql://app:apppass@mysql:3306/app

O nome do host é o nome do contêiner mysql (no Compose, é o nome do serviço), e a porta é 3306 dentro do contêiner. Não é necessário usar -p para conexões entre contêineres.

Gerencie com Compose

Em projetos reais, é recomendável usar Compose. Adicione compose.yaml, .env e o arquivo de configuração 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:
ConfiguraçãoDescrição
TZ: Asia/TaipeiDefine o fuso horário do contêiner; o NOW() do MySQL usará esse fuso horário
./my.cnf:/etc/mysql/conf.d/custom.cnfO MySQL lê os arquivos .cnf em /etc/mysql/conf.d/
$$MYSQL_ROOT_PASSWORD$$ impede que o Compose substitua a variável; ela fica para o shell dentro do contêiner ler a variável de ambiente do contêiner
-h 127.0.0.1Importante, veja a explicação abaixo
$ 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 |
+----+--------------+-------+
  • --wait aguarda o serviço ficar healthy antes de retornar. Foram cerca de 11 segundos até Healthy, em linha com o tempo de inicialização visto antes.
  • max_connections é 300 e buffer_pool_mb é 256; my.cnf foi aplicado.
  • @@system_time_zone é CST (China Standard Time, UTC+8), e NOW() mostra o horário de Taipei.
  • O SQL de inicialização também foi executado; há três registros em products.

Armadilha na verificação de integridade: localhost informa healthy cedo demais

Uma forma comum de verificar a integridade do MySQL na internet é mysqladmin ping -h localhost. Testamos trocando o 127.0.0.1 acima 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
  • Após apenas 5.8 segundos, o serviço informa Healthy, mas logo em seguida a conexão com root falha. O log mostra que o processo ainda estava em Temporary server started.
  • Só funcionou quando tentamos de novo 10 segundos depois.

O motivo é que o servidor temporário durante a inicialização só aceita conexões por socket, e -h localhost usa justamente o socket. Por isso, o ping recebe uma resposta. Além disso, mysqladmin ping considera bem-sucedido sempre que o servidor responde, mesmo que ainda não seja possível entrar com usuário e senha. Assim, o Compose acredita que o banco de dados já está pronto e inicia os serviços que dependem dele (depends_on com condition: service_healthy); na primeira conexão, o aplicativo pode falhar.

Com -h 127.0.0.1, a conexão usa TCP. O servidor temporário não aceita conexões de rede (port: 0), então o serviço só informa healthy depois que o servidor definitivo inicia.

Backup e restauração

Exporte o SQL com mysqldump, exclua uma tabela de propósito e depois restaure-a a partir do backup:

$ 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 |
+----+--------------+-------+
  • O comando de backup é envolvido por sh -c '...', e $MYSQL_ROOT_PASSWORD é expandido dentro do contêiner. Assim, a senha não aparece no histórico de comandos do host. As aspas simples externas impedem que o shell do host substitua a variável primeiro.
  • --single-transaction exporta as tabelas InnoDB em um snapshot consistente, sem bloqueá-las durante o backup. Em produção, sempre inclua essa opção.
  • O arquivo de backup app.sql tem 2.0K e contém 1 comando INSERT INTO (os três registros são gravados em um único INSERT).
  • Depois de DROP TABLE, a consulta retorna Table 'app.products' doesn't exist.
  • Para restaurar, use docker exec -i e envie app.sql, que está no host, ao mysql dentro do contêiner pela entrada padrão. Os dados são restaurados por completo.

Na restauração, use apenas -i, não -it. Quando a entrada padrão é um arquivo, e não um terminal, -t causa um erro:

$ docker exec -it mysql sh -c "echo hi" < /dev/null
cannot attach stdin to a TTY-enabled container because stdin is not a terminal

Outros comandos de backup comuns:

# Fazer backup de todos os bancos de dados (incluindo usuários e permissões)
docker exec mysql sh -c 'mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --all-databases --single-transaction' > all.sql

# Fazer backup e compactar
docker exec mysql sh -c 'mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --single-transaction app' | gzip > app.sql.gz

# Restaurar um backup compactado
gunzip < app.sql.gz | docker exec -i mysql sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" app'

Backup e restauração para usuários do Windows

O PowerShell não oferece suporte a <, e o > do Windows PowerShell 5.1 salva os arquivos com codificação UTF-16, causando erros na importação. Em vez disso, gere o arquivo dentro do contêiner e depois copie-o com docker cp. Esse método funciona em todos os sistemas:

# Backup: exporte para /tmp/app.sql dentro do contêiner e copie o arquivo para fora
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

# Restauração: copie o arquivo para dentro e importe-o no contêiner
docker cp app.sql mysql:/tmp/restore.sql
docker exec mysql sh -c 'mysql -uroot -p$MYSQL_ROOT_PASSWORD app < /tmp/restore.sql'
# Backup: exporte para /tmp/app.sql dentro do contêiner e copie o arquivo para fora
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

# Restauração: copie o arquivo para dentro e importe-o no contêiner
docker cp app.sql mysql:/tmp/restore.sql
docker exec mysql sh -c 'mysql -uroot -p$MYSQL_ROOT_PASSWORD app < /tmp/restore.sql'
REM Backup: exporte para /tmp/app.sql dentro do contêiner e copie o arquivo para fora
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 Restauração: copie o arquivo para dentro e importe-o no contêiner
docker cp app.sql mysql:/tmp/restore.sql
docker exec mysql sh -c "mysql -uroot -p$MYSQL_ROOT_PASSWORD app < /tmp/restore.sql"
  • --result-file faz com que mysqldump grave diretamente no arquivo, sem redirecionamento pelo terminal.
  • O < do comando fica dentro da string de sh -c e é processado pelo shell dentro do contêiner. Por isso, o comando também funciona no PowerShell.
  • Não coloque aspas em -p$MYSQL_ROOT_PASSWORD para evitar problemas com aspas internas no PowerShell 5.1 (a senha não deve conter espaços).

Este conjunto de comandos foi testado no Docker 29 e restaurou os 3 registros por completo:

$ 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

Teste prático: alterei a variável de senha, mas não funcionou

Variáveis como MYSQL_ROOT_PASSWORD são usadas somente na primeira inicialização (quando o Volume está vazio). Remova o contêiner, mantenha o Volume e crie o contêiner novamente com outra senha:

$ 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 database aparece 0 vezes nos logs: o Volume já contém dados, então o script de inicialização foi ignorado.
  • Não é possível entrar com a nova senha different; a senha antiga secret ainda funciona.

Isso significa que, depois que os dados existem, alterar MYSQL_ROOT_PASSWORD não tem efeito. Para alterar a senha, entre com a senha antiga e execute o SQL:

ALTER USER 'root'@'%' IDENTIFIED BY 'NovaSenha';
ALTER USER 'root'@'localhost' IDENTIFIED BY 'NovaSenha';

Ou, se não precisar manter os dados, remova o Volume e inicialize novamente (docker compose down -v; todos os dados serão perdidos).

Solução 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)
  • A senha está incorreta ou o Volume foi inicializado com uma senha antiga (veja o teste prático acima).
  • Por padrão, root tem duas contas, root@% e root@localhost, que permitem conexões de qualquer host. Para usuários criados por você, confirme se o host é %; caso contrário, eles só poderão se conectar de um host específico.

Can't connect to local MySQL server through socket

O cliente trata localhost como uma conexão por socket. Use 127.0.0.1 (veja o teste prático anterior).

O contêiner encerra logo após iniciar

Veja os logs: docker logs mysql. Causas comuns:

  • MYSQL_ROOT_PASSWORD não foi definida (e nem MYSQL_ALLOW_EMPTY_PASSWORD ou MYSQL_RANDOM_ROOT_PASSWORD).
  • O Volume contém dados inicializados por outra versão (por exemplo, 5.7 ou MariaDB), incompatíveis com a versão atual.
  • Falta memória no host.

Atualização da versão 5.7 / 8.0

Não monte diretamente um Volume antigo na imagem de uma versão mais nova. Primeiro, exporte os dados com mysqldump, importe-os em um banco de dados vazio da nova versão e valide tudo em um ambiente de teste.

Resumo

  • Coloque o SQL de inicialização em /docker-entrypoint-initdb.d/; ele é executado somente na primeira inicialização.
  • Para se conectar pelo host, use 127.0.0.1; localhost usa uma conexão por socket.
  • Configure a verificação de integridade com mysqladmin ping -h 127.0.0.1.
  • Faça backups com mysqldump --single-transaction e teste a restauração.

Exercícios

Exercício 1: liste todos os bancos de dados no contêiner MySQL com um único comando.

Resposta
docker exec mysql sh -c 'mysql -uroot -p$MYSQL_ROOT_PASSWORD -e "SHOW DATABASES"'

Exercício 2: a configuração do MySQL no Compose define ports: "127.0.0.1:3306:3306". Qual nome de host e porta um aplicativo no mesmo projeto Compose deve usar para se conectar?

Resposta

O nome do host é o nome do serviço db, e a porta é 3306, dentro do contêiner. ports afeta somente as conexões feitas pelo host; os contêineres não precisam dessa configuração para se conectar entre si.

Perguntas frequentes

O texto em chinês aparece corrompido?

No MySQL 8, o padrão já é utf8mb4. A corrupção geralmente vem do conjunto de caracteres da conexão do cliente ou da codificação do arquivo importado; no Windows PowerShell 5.1, um backup salvo com > fica em UTF-16. Use a forma com docker cp mostrada neste capítulo.

Devo usar MySQL ou MariaDB?

Os dois são altamente compatíveis. Programas comuns, como o WordPress, oferecem suporte a ambos; no MariaDB, as variáveis de ambiente começam com MARIADB_.

A alteração de MYSQL_ROOT_PASSWORD não teve efeito?

A variável de senha só é usada quando o Volume está vazio. Depois disso, altere a senha com SQL. Veja o teste prático neste capítulo.

Atualizado 14 de outubro de 2026