目次

DockerでMySQLを実行する:インストール、接続、バックアップと復元

1行のコマンドで起動する

まず mysql-demo フォルダを用意し、その中の initdb/ に初期化用のSQLファイルを2つ置きます(後で説明します)。

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

mysql-demo フォルダでMySQLを起動します。

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

Windowsでの書き方(タブをクリックして切り替えます):

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
パラメーター説明
MYSQL_ROOT_PASSWORDrootのパスワード(必須。MYSQL_RANDOM_ROOT_PASSWORD=yes によるランダム生成も可能)
MYSQL_DATABASE初回起動時に作成するデータベース
MYSQL_USER / MYSQL_PASSWORD一般ユーザーを作成し、MYSQL_DATABASE に対するすべての権限を付与します
-p 127.0.0.1:3306:3306ローカルからのみアクセスでき、外部からは接続できません
-v mysql_data:/var/lib/mysqlデータは mysql_data という名前のVolumeに保存されます
-v .../initdb:/docker-entrypoint-initdb.d:ro初期化時に実行するSQLです

初回起動時に行われること

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

ログを見ると、イメージの起動スクリプトは初回起動時(データディレクトリが空の場合)に次の順序で処理します。

  1. データベースファイルを作成します(Initializing database files)。
  2. 一時サーバーを起動します(Starting temporary server)。port: 0 に注目してください。一時サーバーはネットワーク接続を受け付けず、ソケット経由でのみ接続できます。
  3. データベース app とユーザー app を作成し、アクセス権を付与します。
  4. ファイル名の順に /docker-entrypoint-initdb.d/ 内の 01-schema.sql、02-seed.sql を実行します。
  5. 一時サーバーを停止し、本番用の設定で起動します。最後の行の port: 3306 で、実際に接続できる状態になったことが分かります。

全体で約 10 秒かかります。/docker-entrypoint-initdb.d/ は .sql、.sql.gz、.sh ファイルに対応しており、初回の初期化時にのみ実行されます。その後コンテナを再起動しても、再実行されません。

バージョンの選択

tag説明
mysql:8.4長期サポート版(LTS)。本番環境での使用を推奨します
mysql:9最新機能を含む新しいリリースですが、サポート期間は短めです
mariadb:11MariaDB。MySQL と高い互換性を持つフォークで、環境変数のプレフィックスは MARIADB_ です

接続

コンテナ内から

コンテナ内には mysql クライアントが用意されています。対話形式で操作するには docker exec -it mysql mysql -uapp -p を実行し、パスワードを入力すると mysql> プロンプトが表示されます。-e を使って SQL を直接実行することもできます。

$ 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 はテーブル形式で出力します(非対話モードではデフォルトでタブ区切りです)。
  • 日本語データは正しく扱われています。サーバーの文字セットは utf8mb4、照合順序は utf8mb4_0900_ai_ci です。これは MySQL 8 のデフォルト値なので、別途設定する必要はありません。日本語を含む行の罫線が揃っていないのは、mysql クライアントによる日本語の幅の計算方法が原因で、データ自体に問題はありません。
  • 毎回表示される [Warning] Using a password on the command line interface can be insecure. は、コマンドに書いたパスワードがプロセス一覧から他のユーザーに見える可能性があるという警告です。ここでは無視してかまいません。

ホストから:localhost と 127.0.0.1 の違い

ホスト上の MySQL クライアント(MySQL Workbench、DBeaver、mysql コマンドなど)から 127.0.0.1:3306 に接続します。localhost ではなく 127.0.0.1 を指定してください。--network host を使うコンテナで、ホスト上の mysql クライアントの動作を再現して確認します。

$ 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:失敗し、Can't connect to local MySQL server through socket と表示されます。MySQL クライアントは localhost を指定されると、TCP ではなく Unix ソケットファイルで接続します。しかしホスト上にはそのソケットファイルがありません(コンテナ内にあります)。
  • -h 127.0.0.1:成功し、-p に対応するポートを経由して TCP 接続します。
  • USER() が app@172.18.0.1 を表示しています。MySQL から見ると、接続元は 127.0.0.1 ではなく Docker ネットワークのゲートウェイ IP です。CURRENT_USER() の app@% は、このアカウントが任意のホストからの接続を許可されていることを示します。

他のコンテナから

MySQL とアプリケーションを同じユーザー定義ネットワークに接続すると、コンテナ名で接続できます(デフォルトの bridge ネットワークでは名前解決できません。ネットワークとコンテナ間通信を参照してください)。

$ 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

アプリケーションの接続文字列は次のようになります。

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

ホスト名にはコンテナ名 mysql(Compose ではサービス名)を使い、ポートにはコンテナ内の 3306 を指定します。コンテナ間の接続に -p は必要ありません。

Compose で管理する

実際のプロジェクトでは Compose で管理することをおすすめします。mysql-demo に compose.yaml、.env、カスタム設定ファイル my.cnf を追加します。

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:
設定説明
TZ: Asia/Taipeiコンテナのタイムゾーンを設定します。MySQL の NOW() はこのタイムゾーンを使用します
./my.cnf:/etc/mysql/conf.d/custom.cnfMySQL は /etc/mysql/conf.d/ 内の .cnf ファイルを読み込みます
$$MYSQL_ROOT_PASSWORD$$ を使うと Compose による変数置換を防ぎ、コンテナ内の shell にコンテナの環境変数を読み取らせます
-h 127.0.0.1重要。下の説明を参照してください
$ 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 はサービスが healthy になるまで待ってから戻ります。Healthy になるまで約 11 秒かかり、先ほど確認した初期化時間と一致します。
  • max_connections は 300、buffer_pool_mb は 256 で、my.cnf が反映されています。
  • @@system_time_zone は CST(中国標準時、UTC+8)で、NOW() は台北時間を表示しています。
  • 初期化 SQL も実行され、products に 3 件のデータがあります。

ヘルスチェックの落とし穴:localhost では healthy の報告が早すぎる

インターネット上でよく見かける MySQL のヘルスチェック方法は mysqladmin ping -h localhost です。上記の 127.0.0.1 を 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
  • わずか 5.8 秒 で Healthy と報告されましたが、直後に root で接続すると失敗し、ログにはまだ Temporary server started と表示されています。
  • さらに 10 秒待ってから試すと、成功しました。

原因は、初期化中の一時サーバーには socket 経由でしか接続できず、-h localhost は socket を使うため、ping に応答してしまうことです。また、mysqladmin ping はサーバーが応答すれば成功とみなすため、アカウントとパスワードでまだログインできなくても成功します。その結果、Compose はデータベースの準備ができたと判断して、依存するサービス(depends_on の condition: service_healthy)を起動します。アプリケーションの最初の接続が失敗する可能性があります。

-h 127.0.0.1 に変更すると TCP が使われます。一時サーバーはネットワーク接続を受け付けない(port: 0)ため、正式なサーバーの起動後に初めて healthy と報告されます。

バックアップと復元

mysqldump で SQL をエクスポートし、テーブルをわざと削除してから、バックアップから復元します。

$ 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 |
+----+--------------+-------+
  • バックアップコマンドは sh -c '...' で囲みます。$MYSQL_ROOT_PASSWORD はコンテナ内で展開されるため、パスワードがホストのコマンド履歴に残りません。外側をシングルクォートで囲み、ホストのシェルが先に変数を置き換えないようにします。
  • --single-transaction を指定すると、InnoDB のテーブルを一貫性のあるスナップショットとしてエクスポートできます。バックアップ中にテーブルがロックされないため、本番環境では必ず指定してください。
  • バックアップファイル app.sql は 2.0K で、INSERT INTO コマンドを 1 つ含みます(3 件のデータは同じ INSERT に書き込まれています)。
  • DROP TABLE の後にクエリを実行すると、Table 'app.products' doesn't exist が返ります。
  • 復元には docker exec -i を使い、ホスト上の app.sql を標準入力経由でコンテナ内の mysql に渡します。データが完全に復元されます。

復元時は -i だけを使い、-it は使いません。標準入力がターミナルではなくファイルの場合、-t を指定するとエラーになります。

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

その他のよく使うバックアップコマンド:

# すべてのデータベースをバックアップ(ユーザーと権限を含む)
docker exec mysql sh -c 'mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --all-databases --single-transaction' > all.sql

# バックアップして圧縮
docker exec mysql sh -c 'mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --single-transaction app' | gzip > app.sql.gz

# 圧縮したバックアップを復元
gunzip < app.sql.gz | docker exec -i mysql sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" app'

Windows ユーザー向けのバックアップと復元

PowerShell は < をサポートしていません。また、Windows PowerShell 5.1 の > はファイルを UTF-16 エンコードで保存するため、インポート時にエラーになります。コンテナ内でファイルを作成し、docker cp でコピーする方法に変更してください。この方法はどのシステムでも使えます。

# バックアップ:コンテナ内で /tmp/app.sql にエクスポートしてからコピー
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

# 復元:先にコンテナへコピーし、その後コンテナ内でインポート
docker cp app.sql mysql:/tmp/restore.sql
docker exec mysql sh -c 'mysql -uroot -p$MYSQL_ROOT_PASSWORD app < /tmp/restore.sql'
# バックアップ:コンテナ内で /tmp/app.sql にエクスポートしてからコピー
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

# 復元:先にコンテナへコピーし、その後コンテナ内でインポート
docker cp app.sql mysql:/tmp/restore.sql
docker exec mysql sh -c 'mysql -uroot -p$MYSQL_ROOT_PASSWORD app < /tmp/restore.sql'
REM バックアップ:コンテナ内で /tmp/app.sql にエクスポートしてからコピー
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 復元:先にコンテナへコピーし、その後コンテナ内でインポート
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 を指定すると、mysqldump はターミナルのリダイレクトを経由せず、直接ファイルに書き込みます。
  • コマンド内の < は sh -c の文字列内にあり、コンテナ内のシェルが処理するため、PowerShell でも実行できます。
  • -p$MYSQL_ROOT_PASSWORD を引用符で囲まないことで、PowerShell 5.1 が引用符を含む文字列を処理する際の問題を避けられます(パスワードに空白を含めないでください)。

このコマンドは Docker 29 で動作確認済みで、3 件のデータを完全に復元できます。

$ 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

実測:パスワード変数を変更しても反映されない

MYSQL_ROOT_PASSWORD などの変数は初回の初期化時のみ(Volume が空のとき)に使われます。コンテナを削除して Volume は残し、別のパスワードを指定してコンテナを再作成します。

$ 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 が 0 回しか現れません。Volume にすでにデータがあるため、起動スクリプトが初期化をスキップしています。
  • 新しいパスワード different ではログインできず、古いパスワード secret は引き続き使えます。

つまり、データが存在する場合、MYSQL_ROOT_PASSWORD を変更しても効果はありません。パスワードを変更するには、古いパスワードでログインして SQL を実行します。

ALTER USER 'root'@'%' IDENTIFIED BY '新しいパスワード';
ALTER USER 'root'@'localhost' IDENTIFIED BY '新しいパスワード';

または、データを残す必要がない場合は Volume を削除して再初期化します(docker compose down -v。データはすべて消去されます)。

トラブルシューティング

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)
  • パスワードが間違っているか、Volume が古いパスワードで初期化されています(上の実測を参照)。
  • root にはデフォルトで root@% と root@localhost の 2 つのアカウントがあり、どのホストからの接続も許可されます。自分で作成したユーザーについては、host が % になっていることを確認してください。そうでない場合、特定のホストからしか接続できません。

Can't connect to local MySQL server through socket

クライアントは localhost をソケット接続として扱います。127.0.0.1 に変更してください(前の実測を参照)。

起動後すぐに終了する

ログを確認します:docker logs mysql。よくある原因は次のとおりです。

  • MYSQL_ROOT_PASSWORD が設定されていません(MYSQL_ALLOW_EMPTY_PASSWORD または MYSQL_RANDOM_ROOT_PASSWORD も設定されていません)。
  • Volume に別のバージョン(たとえば 5.7 や MariaDB)で初期化したデータがあり、現在のバージョンと互換性がありません。
  • ホストのメモリが不足しています。

5.7 / 8.0 からのアップグレード

古い Volume を新しいイメージに直接マウントしないでください。まず mysqldump でエクスポートし、新しいバージョンの空のデータベースにインポートすることをおすすめします。テスト環境で事前に検証してください。

まとめ

  • 初期化 SQL は /docker-entrypoint-initdb.d/ に配置します。初回の初期化時のみ実行されます。
  • ホストから接続する場合は 127.0.0.1 を指定します。localhost はソケット接続に切り替わります。
  • ヘルスチェックには mysqladmin ping -h 127.0.0.1 を指定します。
  • バックアップには mysqldump --single-transaction を使い、実際に復元できることも確認します。

練習問題

練習 1:MySQL コンテナ内のすべてのデータベースを 1 つのコマンドで一覧表示してください。

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

練習 2:Compose の MySQL で ports: "127.0.0.1:3306:3306" を設定しています。同じ Compose プロジェクト内のアプリケーションが接続するとき、ホスト名とポート番号には何を指定しますか?

解答

ホスト名はサービス名 db、ポート番号はコンテナ内の 3306 です。ports はホストからの接続にのみ影響し、コンテナ間の接続には必要ありません。

よくある質問

日本語が文字化けする場合は?

MySQL 8のデフォルトはすでにutf8mb4です。文字化けは通常、クライアント接続の文字セットか、インポートファイルのエンコーディングが原因です。Windows PowerShell 5.1で > を使って保存したバックアップはUTF-16です。本章の docker cp を使う方法を使ってください。

MySQLとMariaDBのどちらを使うべきですか?

両者は高い互換性があります。WordPressなどの一般的なプログラムはどちらもサポートしています。MariaDBでは環境変数の先頭が MARIADB_ に変わります。

MYSQL_ROOT_PASSWORDを変更しても反映されない場合は?

パスワード変数が使われるのはVolumeが空のときだけです。その後はSQLで変更します。実測結果は本章を参照してください。

更新日 2026年10月14日