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 utf8mb4 from the start

Document the root password in a secrets manager — not in shell history or plain text files.

Windows Installation

  1. Download MySQL Installer from dev.mysql.com/downloads/installer
  2. Choose Server only or Developer Default (includes Workbench)
  3. 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

  1. Use package manager or official repos — avoid compiling from source unless necessary
  2. Separate OS user mysql owns data files — never run mysqld as root
  3. Enable automatic security updates for the OS; plan MySQL minor version upgrades quarterly
  4. Store backups on a separate volume or object storage from day one
  5. 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.