Install and Configure MySQL on Ubuntu for WordPress
MySQL is a common database choice for WordPress and many PHP applications. On Ubuntu, you can install it from the default package repositories and then apply a small performance configuration for local web hosting.
This guide uses generic placeholders for usernames and passwords. Do not hard-code real production passwords in public scripts, documentation, Git repositories, or screenshots.
What the Script Does
The deployment script this article is based on:
- Installs
mysql-server. - Enables and starts the MySQL service.
- Creates an admin user for local socket access.
- Backs up the MySQL configuration.
- Writes a custom performance config file.
- Validates the MySQL configuration.
- Restarts MySQL and tests login again.
Install MySQL
Run:
sudo apt update sudo DEBIAN_FRONTEND=noninteractive apt install -y mysql-server sudo systemctl enable mysql sudo systemctl start mysql sudo systemctl is-active --quiet mysql
You can open the MySQL shell as root with:
sudo mysql
Create an Admin User
Use your own username and a strong password:
MYSQL_ADMIN_USER="dbadmin" MYSQL_ADMIN_PASSWORD="<REPLACE_WITH_STRONG_PASSWORD>"
Create the user:
sudo mysql <<MYSQL_SCRIPT
CREATE USER IF NOT EXISTS '${MYSQL_ADMIN_USER}'@'localhost' IDENTIFIED BY '${MYSQL_ADMIN_PASSWORD}';
GRANT ALL PRIVILEGES ON *.* TO '${MYSQL_ADMIN_USER}'@'localhost' WITH GRANT OPTION;
FLUSH PRIVILEGES;
MYSQL_SCRIPT
Test the user:
mysql --protocol=socket -u "${MYSQL_ADMIN_USER}" -p -e "SELECT VERSION();"
Type the password when prompted.
Avoid putting the real password directly on the command line, because command-line arguments may be visible in shell history or process listings.
Back Up the MySQL Config
Create a backup folder:
sudo mkdir -p /opt/server-backups/mysql timestamp="$(date +%Y%m%d%H%M%S)"
Back up the main MySQL config if it exists:
if [ -f /etc/mysql/mysql.conf.d/mysqld.cnf ]; then
sudo cp /etc/mysql/mysql.conf.d/mysqld.cnf "/opt/server-backups/mysql/mysqld.cnf.before.${timestamp}.bak"
fi
Write a Custom Performance Config
Create a custom config file:
sudo tee /etc/mysql/mysql.conf.d/99-custom-performance.cnf >/dev/null <<EOF [mysqld] port = 3306 bind-address = 127.0.0.1 innodb_buffer_pool_size = 4G max_connections = 200 tmp_table_size = 256M max_heap_table_size = 256M innodb_flush_log_at_trx_commit = 1 innodb_file_per_table = 1 skip_name_resolve = ON EOF
The bind-address = 127.0.0.1 setting keeps MySQL listening locally. That is a good default when web applications connect from the same server.
For remote administration, use an SSH tunnel instead of exposing MySQL directly to the internet.
Validate and Restart MySQL
Validate the config:
sudo mysqld --validate-config
If validation succeeds, restart MySQL:
sudo systemctl restart mysql sudo systemctl is-active --quiet mysql
Test login again:
mysql --protocol=socket -u "${MYSQL_ADMIN_USER}" -p -e "SELECT VERSION();"
Back up the custom config:
sudo cp /etc/mysql/mysql.conf.d/99-custom-performance.cnf "/opt/server-backups/mysql/99-custom-performance.cnf.after.${timestamp}.bak"
Full Script
Here is the full Bash script used for this MySQL install workflow. The password value is intentionally shown as a placeholder; replace it before running the script:
#!/usr/bin/env bash
__show_script_usage() {
cat <<'__SCRIPT_USAGE__'
# Install-MySQL.sh
Installs and configures MySQL for the Linux WordPress hosting stack.
## Example Usage
```bash
cd /opt/DevOps/Scripts
sudo bash ./Install-MySQL.sh
```
## Notes
This changes MySQL server configuration and restarts MySQL. It also writes backups under `/opt/DevOps/Backups/MySQL`.
__SCRIPT_USAGE__
}
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
case "${1:-}" in
-h|--help|--usage)
__show_script_usage
exit 0
;;
esac
fi
# ============================================================
# Variables
# ============================================================
MYSQL_ADMIN_USER="mysqladmin"
MYSQL_ADMIN_PASSWORD='<REPLACE_WITH_STRONG_PASSWORD>'
MYSQL_PORT="3306"
MYSQL_BIND_ADDRESS="127.0.0.1"
MYSQL_INNODB_BUFFER_POOL_SIZE="4G"
MYSQL_MAX_CONNECTIONS="200"
MYSQL_TMP_TABLE_SIZE="256M"
MYSQL_MAX_HEAP_TABLE_SIZE="256M"
DEVOPS_BACKUP_DIR="/opt/DevOps/Backups/MySQL"
# ============================================================
# Safety
# ============================================================
set -euo pipefail
echo "Installing and configuring MySQL..."
if [ "$EUID" -ne 0 ]; then
echo "ERROR: Run with sudo."
exit 1
fi
# ============================================================
# Install MySQL
# ============================================================
echo "Updating package lists..."
apt update
echo "Installing MySQL Server..."
DEBIAN_FRONTEND=noninteractive apt install -y mysql-server
echo "Enabling and starting MySQL..."
systemctl enable mysql
systemctl start mysql
echo "Checking MySQL service..."
systemctl is-active --quiet mysql
# ============================================================
# Create MySQL Admin User
# ============================================================
echo "Creating MySQL admin user for HeidiSQL..."
sudo mysql <<MYSQL_SCRIPT
CREATE USER IF NOT EXISTS '${MYSQL_ADMIN_USER}'@'localhost' IDENTIFIED BY '${MYSQL_ADMIN_PASSWORD}';
GRANT ALL PRIVILEGES ON *.* TO '${MYSQL_ADMIN_USER}'@'localhost' WITH GRANT OPTION;
FLUSH PRIVILEGES;
MYSQL_SCRIPT
echo "Testing MySQL admin user..."
mysql --protocol=socket -u "${MYSQL_ADMIN_USER}" -p"${MYSQL_ADMIN_PASSWORD}" -e "SELECT VERSION();" >/dev/null
# ============================================================
# Backup Config
# ============================================================
echo "Creating MySQL backup directory..."
mkdir -p "${DEVOPS_BACKUP_DIR}"
TIMESTAMP="$(date +%Y%m%d%H%M%S)"
if [ -f /etc/mysql/mysql.conf.d/mysqld.cnf ]; then
echo "Backing up original mysqld.cnf..."
cp /etc/mysql/mysql.conf.d/mysqld.cnf "${DEVOPS_BACKUP_DIR}/mysqld.cnf.before.${TIMESTAMP}.bak"
fi
# ============================================================
# Configure MySQL
# ============================================================
echo "Writing MySQL performance configuration..."
cat > /etc/mysql/mysql.conf.d/99-custom-performance.cnf <<EOF
[mysqld]
port = ${MYSQL_PORT}
bind-address = ${MYSQL_BIND_ADDRESS}
innodb_buffer_pool_size = ${MYSQL_INNODB_BUFFER_POOL_SIZE}
max_connections = ${MYSQL_MAX_CONNECTIONS}
tmp_table_size = ${MYSQL_TMP_TABLE_SIZE}
max_heap_table_size = ${MYSQL_MAX_HEAP_TABLE_SIZE}
innodb_flush_log_at_trx_commit = 1
innodb_file_per_table = 1
skip_name_resolve = ON
EOF
cp /etc/mysql/mysql.conf.d/99-custom-performance.cnf "${DEVOPS_BACKUP_DIR}/99-custom-performance.cnf.after.${TIMESTAMP}.bak"
# ============================================================
# Validate Config
# ============================================================
echo "Validating MySQL configuration..."
mysqld --validate-config
# ============================================================
# Restart MySQL
# ============================================================
echo "Restarting MySQL..."
systemctl restart mysql
echo "Checking MySQL status..."
systemctl is-active --quiet mysql
echo "Testing MySQL admin user after restart..."
mysql --protocol=socket -u "${MYSQL_ADMIN_USER}" -p"${MYSQL_ADMIN_PASSWORD}" -e "SELECT VERSION();" >/dev/null
# ============================================================
# Complete
# ============================================================
echo "MySQL install and configuration complete."
echo "MySQL root access: sudo mysql"
echo "MySQL admin user: ${MYSQL_ADMIN_USER}"
echo "MySQL bind address: ${MYSQL_BIND_ADDRESS}"
echo "MySQL port: ${MYSQL_PORT}"
echo "Use HeidiSQL through SSH tunnel with host localhost and user ${MYSQL_ADMIN_USER}."
Troubleshooting
If MySQL does not restart, check:
sudo journalctl -u mysql -xe sudo mysqld --validate-config
If login fails, confirm the user exists:
sudo mysql -e "SELECT user, host FROM mysql.user;"
Quick Reference
sudo apt install -y mysql-server sudo systemctl enable mysql sudo mysqld --validate-config sudo systemctl restart mysql sudo mysql
Keep database credentials private. Use placeholders in documentation, prompt for passwords when possible, and never publish real passwords in scripts.