Installing MySQL
Pre-Installation Planning
Before installing MySQL, decide:
- Version — MySQL 8.0 LTS for new projects; verify application driver compatibility
- Role — dedicated DB server vs co-located with application
- Resources — minimum 2 GB RAM for development; 8+ GB for production
- Character set — configure
utf8mb4from the start
Document the root password in a secrets manager — not in shell history or plain text files.
Windows Installation
- Download MySQL Installer from dev.mysql.com/downloads/installer
- Choose Server only or Developer Default (includes Workbench)
- During setup:
- Select MySQL Server 8.0
- Set root password and optionally create additional users
- Configure as Windows Service with automatic start
- Enable TCP/IP on port 3306
# Verify service
Get-Service MySQL80
# Connect from command line
mysql -u root -p -h localhost
# Add to PATH if needed (default install location)
# C:\Program Files\MySQL\MySQL Server 8.0\bin
Windows firewall: restrict port 3306 to application servers only — never expose MySQL to the public internet.
Linux — Ubuntu/Debian (apt)
sudo apt update
sudo apt install mysql-server
# Start and enable
sudo systemctl start mysql
sudo systemctl enable mysql
# Secure installation
sudo mysql_secure_installation
The secure installation script prompts for:
- VALIDATE PASSWORD component (recommended: enable)
- Root password (if using
auth_socket, switch to password auth for remote admin) - Remove anonymous users — Yes
- Disallow root remote login — Yes (use SSH tunnel or bastion)
- Remove test database — Yes
- Reload privilege tables — Yes
# Check status
sudo systemctl status mysql
mysql --version
# Initial connection (Ubuntu uses auth_socket for root)
sudo mysql
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'SecurePass123!';
FLUSH PRIVILEGES;
Linux — RHEL/CentOS/Rocky (yum/dnf)
sudo dnf install https://dev.mysql.com/get/mysql80-community-release-el9-1.noarch.rpm
sudo dnf install mysql-community-server
sudo systemctl start mysqld
sudo systemctl enable mysqld
# Temporary root password in log
sudo grep 'temporary password' /var/log/mysqld.log
mysql -u root -p
ALTER USER 'root'@'localhost' IDENTIFIED BY 'NewSecurePass123!';
macOS (Homebrew)
brew install mysql
brew services start mysql
# Secure (set root password)
mysql_secure_installation
# Connect
mysql -u root -p
Apple Silicon (M1/M2) runs MySQL natively via Homebrew — no Rosetta required.
Post-Install Configuration
Edit /etc/mysql/mysql.conf.d/mysqld.cnf (Ubuntu) or /etc/my.cnf:
[mysqld]
bind-address = 127.0.0.1
port = 3306
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
max_connections = 200
innodb_buffer_pool_size = 1G
log_error = /var/log/mysql/error.log
slow_query_log = 1
long_query_time = 2
[mysql]
default-character-set = utf8mb4
[client]
default-character-set = utf8mb4
Restart after changes:
sudo systemctl restart mysql
Create Application User
CREATE DATABASE myapp CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'app_secure_password';
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.* TO 'app_user'@'localhost';
FLUSH PRIVILEGES;
For remote application servers:
CREATE USER 'app_user'@'10.0.1.%' IDENTIFIED BY 'app_secure_password';
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.* TO 'app_user'@'10.0.1.%';
Verification Checklist
SELECT VERSION();
SHOW VARIABLES LIKE 'character_set_server';
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
SHOW DATABASES;
# Test application user
mysql -u app_user -p myapp -e "SELECT 1;"
Docker (Development)
docker run -d \
--name mysql-dev \
-e MYSQL_ROOT_PASSWORD=devroot \
-e MYSQL_DATABASE=myapp \
-e MYSQL_USER=app \
-e MYSQL_PASSWORD=apppass \
-p 3306:3306 \
mysql:8.0 \
--character-set-server=utf8mb4 \
--collation-server=utf8mb4_unicode_ci
Mount a volume for persistence: -v mysql_data:/var/lib/mysql
Best Practices
- Use package manager or official repos — avoid compiling from source unless necessary
- Separate OS user
mysqlowns data files — never run mysqld as root - Enable automatic security updates for the OS; plan MySQL minor version upgrades quarterly
- Store backups on a separate volume or object storage from day one
- Document connection strings and credentials in your team’s secrets vault
Common Mistakes
| Mistake | Fix |
|---|---|
| Leaving root without password | Run mysql_secure_installation |
bind-address = 0.0.0.0 without firewall |
Bind to private IP; restrict with iptables/security groups |
| Skipping charset configuration | Set utf8mb4 before creating tables |
Using default max_connections = 151 at scale |
Tune based on connection pool sizing |
Troubleshooting
Port 3306 already in use:
sudo lsof -i :3306
sudo systemctl stop mysql # or kill conflicting process
InnoDB initialization failure:
sudo chown -R mysql:mysql /var/lib/mysql
sudo systemctl start mysql
“Authentication plugin ‘caching_sha2_password’ not supported” — upgrade client library or alter user:
ALTER USER 'app_user'@'%' IDENTIFIED WITH mysql_native_password BY 'password';
Production Scenario
A DevOps engineer provisions MySQL on Ubuntu 22.04 via Ansible: installs mysql-server, deploys custom my.cnf with tuned buffer pool, creates application users, configures mysqld_exporter for Prometheus, and validates with a smoke-test migration before handing off to the development team.
Proper installation and hardening prevent the most common production incidents — exposed databases, weak credentials, and charset migration nightmares.