A production-ready MySQL 8.0 setup with Docker Compose, including comprehensive configuration, monitoring, backup scripts, and phpMyAdmin.
- ✅ MySQL 8.0 (latest stable version)
- ✅ Production-optimized configuration
- ✅ Data persistence with volumes
- ✅ Health checks and auto-restart
- ✅ Resource limits (CPU and Memory)
- ✅ Logging configuration
- ✅ Network isolation
- ✅ phpMyAdmin for database management
- ✅ Custom initialization scripts
- ✅ Automated backup script
- ✅ Security best practices
.
├── docker-compose.yml # Main Docker Compose configuration
├── .env # Environment variables (create from .env.example)
├── .env.example # Example environment file
├── backup.sh # Automated backup script
├── mysql/
│ ├── conf.d/ # Custom MySQL configuration
│ │ └── custom.cnf # Performance and security settings
│ ├── init/ # Initialization scripts (runs once)
│ │ └── 01-init.sql # Example initialization script
│ ├── data/ # Database data (auto-created)
│ └── backups/ # Backup storage directory
└── README.md # This file
- Docker Engine 20.10+
- Docker Compose 2.0+
- At least 2GB free RAM
- 10GB free disk space
# Clone or create project directory
mkdir mysql-production && cd mysql-production
# Copy environment example file
cp .env.example .env
# Edit .env file with your secure passwords
nano .env # or vim, code, etc.Edit the .env file and set secure passwords:
MYSQL_ROOT_PASSWORD=YourVerySecureRootPassword123!
MYSQL_DATABASE=production_db
MYSQL_USER=app_user
MYSQL_PASSWORD=YourSecureAppPassword123!
MYSQL_PORT=3306
TZ=Asia/Singapore
MYSQL_DATA_PATH=./mysql/data
PHPMYADMIN_PORT=8080Important Security Notes:
- Use strong passwords (minimum 16 characters)
- Mix uppercase, lowercase, numbers, and special characters
- Never commit
.envfile to version control - Add
.envto your.gitignorefile
# Start MySQL and phpMyAdmin
docker-compose up -d
# Check status
docker-compose ps
# View logs
docker-compose logs -f mysql
# Check health
docker-compose exec mysql mysqladmin ping -u root -p# Connect to MySQL
docker-compose exec mysql mysql -u root -p
# Or use the app user
docker-compose exec mysql mysql -u app_user -p production_db
# Access phpMyAdmin
# Open browser: http://localhost:8080The docker-compose.yml includes optimized MySQL parameters:
--max_connections=200 # Maximum concurrent connections
--max_allowed_packet=256M # Maximum packet size
--innodb_buffer_pool_size=1G # InnoDB buffer pool size
--innodb_log_file_size=256M # InnoDB log file size
--slow_query_log=1 # Enable slow query logging
--long_query_time=2 # Log queries taking >2 secondsCPU: 1-2 cores
Memory: 1GB minimum, 2GB maximumAdjust in docker-compose.yml based on your needs:
deploy:
resources:
limits:
cpus: "2.0"
memory: 2GAutomatic health monitoring every 10 seconds:
- Checks MySQL availability
- Auto-restarts on failure
- 30-second startup grace period
Connection String Examples:
# From host machine
mysql://app_user:password@localhost:3306/production_db
# From Docker container in same network
mysql://app_user:password@mysql:3306/production_db
# Node.js (Prisma)
DATABASE_URL="mysql://app_user:password@localhost:3306/production_db"
# Python (SQLAlchemy)
SQLALCHEMY_DATABASE_URI=mysql+pymysql://app_user:password@localhost:3306/production_db
# Java (JDBC)
jdbc:mysql://localhost:3306/production_db?user=app_user&password=password# Make backup script executable
chmod +x backup.sh
# Backup specific database
./backup.sh production_db
# Backup with cron (daily at 2 AM)
0 2 * * * /path/to/backup.sh production_db# Backup single database
docker-compose exec mysql mysqldump -u root -p \
--single-transaction \
--routines --triggers --events \
production_db > backup_$(date +%Y%m%d).sql
# Backup all databases
docker-compose exec mysql mysqldump -u root -p \
--all-databases \
--single-transaction > all_databases_$(date +%Y%m%d).sql
# Backup with gzip compression
docker-compose exec mysql mysqldump -u root -p \
production_db | gzip > backup_$(date +%Y%m%d).sql.gz# Restore from uncompressed backup
docker-compose exec -T mysql mysql -u root -p production_db < backup.sql
# Restore from gzipped backup
gunzip < backup.sql.gz | docker-compose exec -T mysql mysql -u root -p production_db
# Restore all databases
docker-compose exec -T mysql mysql -u root -p < all_databases.sql# Access MySQL CLI
docker-compose exec mysql mysql -u root -p
# Create new database
docker-compose exec mysql mysql -u root -p -e "CREATE DATABASE new_db;"
# Grant privileges
docker-compose exec mysql mysql -u root -p -e "
GRANT ALL PRIVILEGES ON new_db.* TO 'app_user'@'%';
FLUSH PRIVILEGES;"
# Show databases
docker-compose exec mysql mysql -u root -p -e "SHOW DATABASES;"
# Show tables
docker-compose exec mysql mysql -u root -p production_db -e "SHOW TABLES;"# View real-time logs
docker-compose logs -f mysql
# Check MySQL status
docker-compose exec mysql mysqladmin -u root -p status
# Show processlist
docker-compose exec mysql mysql -u root -p -e "SHOW PROCESSLIST;"
# Check slow queries
docker-compose exec mysql tail -f /var/log/mysql/slow-query.log
# View error log
docker-compose exec mysql tail -f /var/log/mysql/error.log
# Monitor performance
docker stats mysql-production# Pull latest image
docker-compose pull mysql
# Recreate container
docker-compose up -d mysql
# Verify version
docker-compose exec mysql mysql --version# Stop services
docker-compose down
# Remove volumes (⚠️ DELETES DATA)
docker-compose down -v
# Remove old backups (keeps last 7 days)
find ./mysql/backups -name "*.sql.gz" -mtime +7 -deleteMonitor and adjust these settings in mysql/conf.d/custom.cnf:
[mysqld]
# Increase for more concurrent connections
max_connections=200
# Increase for better performance (75% of available RAM)
innodb_buffer_pool_size=1G
# Tune based on workload
innodb_flush_log_at_trx_commit=2 # 1=safest, 2=faster- Strong Passwords: Use 16+ character passwords with mixed case, numbers, symbols
- Network Isolation: Keep MySQL in isolated Docker network
- Firewall Rules: Restrict port 3306 access to trusted IPs
- Regular Updates: Keep MySQL image updated
- Backup Encryption: Encrypt sensitive backups
- Least Privilege: Create users with minimal required permissions
- Disable Remote Root: Never allow root login from
% - SSL/TLS: Enable encrypted connections for production
-- Create user with specific host
CREATE USER 'app_user'@'172.20.0.%' IDENTIFIED BY 'password';
-- Grant specific privileges only
GRANT SELECT, INSERT, UPDATE, DELETE ON production_db.* TO 'app_user'@'172.20.0.%';# Check logs
docker-compose logs mysql
# Common issues:
# 1. Port already in use
sudo lsof -i :3306
# Change MYSQL_PORT in .env
# 2. Permission issues
sudo chown -R 999:999 ./mysql/data
# 3. Corrupted data
# Backup data and remove volume
docker-compose down -v# Verify container is running
docker-compose ps
# Check health status
docker-compose exec mysql mysqladmin ping -u root -p
# Verify network
docker network ls
docker network inspect mysql-production_mysql_network
# Test connection from host
mysql -h 127.0.0.1 -P 3306 -u app_user -p# Check slow queries
docker-compose exec mysql tail -100 /var/log/mysql/slow-query.log
# Monitor resources
docker stats mysql-production
# Increase buffer pool size in docker-compose.yml
--innodb_buffer_pool_size=2G
# Add indexes to frequently queried columns# Verify volume mount
docker volume inspect mysql-production_mysql_data
# Check directory permissions
ls -la ./mysql/data
# Ensure bind mount path exists
mkdir -p ./mysql/data- Strong, unique passwords set in
.env -
.envfile added to.gitignore - Firewall configured (restrict port 3306)
- SSL/TLS enabled for connections
- Regular backup cron job configured
- Monitoring and alerting set up
- Resource limits tuned for workload
- Slow query log reviewed regularly
- Binary logs configured and rotated
- Separate read-only user created
- phpMyAdmin access restricted (or disabled)
- Health check intervals configured
- Log rotation configured
This configuration is provided as-is for production use.
For issues or questions:
- Check MySQL error logs:
docker-compose logs mysql - Review MySQL documentation
- Check Docker Compose documentation
- Verify environment variables and permissions