MySQL with Docker
This guide shows how to run MySQL in Docker for development and small projects. You'll learn the essential commands, a docker-compose example, how to persist data safely, how to connect, and common pitfalls to avoid.
Concept explained
- A MySQL Docker image packages the database server; the container runs the MySQL daemon process.
- Persistent data must live outside the container filesystem – use Docker volumes (recommended) or host mounts.
- Credentials and initialization can be supplied with environment variables or init scripts.
- For production, prefer managed databases or a PaaS-provided MySQL for backups, monitoring and high availability.
Step-by-step example
- Run a single MySQL container (quick start)
docker network create app_network
docker volume create mysql_data
docker run -d \
--name mysql-dev \
--network app_network \
-e MYSQL_ROOT_PASSWORD=rootsecret \
-e MYSQL_DATABASE=appdb \
-e MYSQL_USER=appuser \
-e MYSQL_PASSWORD=userpass \
-v mysql_data:/var/lib/mysql \
-p 3306:3306 \
mysql:8.0
- Replace passwords with secure values.
- The named volume
mysql_data
holds the DB files so data survives container restarts.
- Connect with mysql client from the host (if mysql client is installed)
mysql -h localhost -P 3306 -u appuser -puserpass appdb
- Exec into the running container to use the bundled mysql client
docker exec -it mysql-dev mysql -u appuser -puserpass appdb
- Minimal docker-compose.yml (recommended for multi-service apps)
services:
db:
image: mysql:8.0
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: strongrootpass
MYSQL_DATABASE: appdb
MYSQL_USER: appuser
MYSQL_PASSWORD: stronguserpass
volumes:
- mysql_data:/var/lib/mysql
networks:
- app_network
volumes:
mysql_data:
networks:
app_network:
Start with: docker compose up -d db
Variations & gotchas
- Init scripts: put .sql or .sh files in a folder mounted to
/docker-entrypoint-initdb.d
– they only run on first initialization. - Character sets: MySQL 8.0 defaults to
utf8mb4
. For older compatibility, you may need to configure charset explicitly. - Authentication: MySQL 8.0 uses
caching_sha2_password
by default; some older clients needmysql_native_password
. - Versions: avoid the
latest
tag. Pin a major/minor version (e.g.,mysql:8.0
) to prevent surprises on updates. - Healthchecks: add a healthcheck in docker-compose to ensure MySQL is ready before your app starts.
# healthcheck example
db:
image: mysql:8.0
healthcheck:
test:
[
"CMD",
"mysqladmin",
"ping",
"-h",
"localhost",
"-u",
"root",
"-p$$MYSQL_ROOT_PASSWORD",
]
timeout: 10s
retries: 5
start_period: 30s
caution
If you change the volume path or recreate the volume, you can lose data. Always back up important databases before volume operations.
Common mistakes
- Exposing 3306 publicly without firewall rules or strong passwords.
- Using weak passwords or committing them into git.
- Running migrations while MySQL isn't fully ready (use healthchecks/wait-for scripts).
- Not setting proper character encoding for international text.
- Using
MYSQL_ALLOW_EMPTY_PASSWORD=yes
in anything beyond local testing.
Best practices
- Use named Docker volumes for persistence.
- Pin image versions (e.g.,
mysql:8.0
). - Keep production credentials in a secret manager or the PaaS secrets feature rather than plain files.
- Schedule regular backups using
mysqldump
and test restores. - Set appropriate
max_connections
,innodb_buffer_pool_size
for your workload. - Use specific users with limited privileges instead of root for application connections.
Example backup command:
docker exec mysql-dev mysqldump -u root -prootsecret appdb > backup.sql
Example restore:
docker exec -i mysql-dev mysql -u root -prootsecret appdb < backup.sql
tip
For local development, consider using a .env
file for database credentials and add it to .gitignore
.
When to use / when not to use
When to use:
- Local development, testing, small internal projects, or prototypes.
- When you want a lightweight DB alongside your app in Docker Compose.
- Legacy applications that specifically require MySQL compatibility.
When not to use:
- High-availability production systems requiring automated backups, replicas, or managed maintenance – prefer a managed DB or the PaaS database service.
Key takeaways
- Run MySQL in Docker using a pinned image, named volume, and explicit credentials.
- Use healthchecks and wait-for logic so apps connect only after MySQL is ready.
- Don't expose MySQL directly to the public internet; secure credentials and regular backups are essential.
- Consider character encoding and authentication plugin compatibility with your application stack.