When I first started managing databases professionally, MySQL was the very first server I ever set up on a Linux box, and to this day it’s still the database I reach for when I need something reliable, well-documented, and battle-tested in production. In this guide, I’m going to walk you through everything I know about installing MySQL on Ubuntu — from the absolute basics all the way to the internal architecture that makes MySQL tick, so you’re not just copy-pasting commands but actually understanding what’s happening under the hood.
Why I Still Choose MySQL in 2026
Before I get into the installation steps, I want to explain why MySQL remains relevant. I’ve worked with PostgreSQL, MariaDB, and a handful of NoSQL engines, but MySQL keeps earning its place because of its predictable performance, mature replication ecosystem, and the sheer size of its community. Whether I’m spinning up a WordPress backend, a Laravel application, or a data warehouse staging layer, MySQL almost always fits the bill.
Understanding MySQL Architecture Before You Install
I always tell people new to databases: don’t just install software blindly — understand what you’re installing. MySQL follows a client-server architecture with a layered design:
- Connection Layer – handles authentication, thread pooling, and SSL negotiation for every client that connects to my server.
- SQL Layer – this is where the parser, optimizer, and query cache (deprecated since MySQL 8.0) live. It’s responsible for turning my SQL text into an execution plan.
- Storage Engine Layer – the pluggable part of MySQL. InnoDB is the default engine I use for almost everything because it supports transactions, row-level locking, and crash recovery.
graph TD
A[Client Application] --> B[Connection Layer]
B --> C[SQL Layer: Parser & Optimizer]
C --> D[Storage Engine Layer]
D --> E[(InnoDB)]
D --> F[(MyISAM)]
D --> G[(Memory)]
E --> H[Disk Storage / Tablespaces]
Knowing this layered structure has saved me countless hours of troubleshooting because I know exactly which layer to check when something misbehaves — is it a connection issue, a query planning issue, or a storage engine issue?
System Requirements I Check Before Installing
Before I install MySQL on any Ubuntu machine, I verify:
| Requirement | Minimum | What I Recommend |
|---|---|---|
| RAM | 1 GB | 4 GB+ for production |
| Disk | 5 GB free | SSD with 50 GB+ for real workloads |
| OS | Ubuntu 20.04+ | Ubuntu 22.04 LTS or 24.04 LTS |
| CPU | 1 core | 2+ cores |
| User privileges | sudo access | sudo access |
Step 1: Update My Package Index
The first thing I always do on a fresh Ubuntu server is update the package index so I’m not installing stale packages.
sudo apt update
sudo apt upgrade -y
Step 2: Installing MySQL Server
I install MySQL directly from Ubuntu’s official APT repository because it’s the most reliable path for most of my use cases.
sudo apt install mysql-server -y
This single command pulls in mysql-server, mysql-client, and the common libraries MySQL depends on. Once it finishes, I confirm the service is running:
sudo systemctl status mysql
Expected output looks something like this:
● mysql.service - MySQL Community Server
Loaded: loaded (/lib/systemd/system/mysql.service; enabled)
Active: active (running) since Thu 2026-07-30 10:12:03 UTC
If I ever want the absolute latest MySQL version (Ubuntu’s default repo often lags a version or two behind), I add Oracle’s official APT repository instead:
wget https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb
sudo dpkg -i mysql-apt-config_0.8.29-1_all.deb
sudo apt update
sudo apt install mysql-server -y
During the dpkg -i step, a text-based configuration screen appears where I select the MySQL version and product line I want (usually “MySQL Server & Cluster” followed by the latest GA release).
Step 3: Securing My Installation
This is a step I never skip, even on a throwaway development VM, because bad habits on dev machines eventually leak into production. MySQL ships with an interactive script that hardens the default configuration.
sudo mysql_secure_installation
It walks me through a series of prompts:
- Validate Password Component – I usually enable this and choose a medium or strong policy.
- Set root password – I always use a long, random password here, generated with a password manager.
- Remove anonymous users – Yes, always.
- Disallow root login remotely – Yes, unless I have a very specific reason not to.
- Remove test database – Yes, I never need the sample
testdatabase in production. - Reload privilege tables – Yes, so all changes take effect immediately.
Step 4: Logging In and Verifying My Installation
sudo mysql -u root -p
Once inside the MySQL shell, I like to run a few sanity checks:
SELECT VERSION();
STATUS;
SHOW DATABASES;
Sample output:
+-----------+
| VERSION() |
+-----------+
| 8.0.40 |
+-----------+
Step 5: Creating a Dedicated Admin User
I never keep using the root account for daily work. Instead, I create a dedicated administrative user with a strong password and scoped privileges:
CREATE USER 'ahmad_admin'@'localhost' IDENTIFIED BY 'Str0ngP@ssw0rd!';
GRANT ALL PRIVILEGES ON *.* WITH GRANT OPTION;
FLUSH PRIVILEGES;
Step 6: Configuring MySQL to Start on Boot
sudo systemctl enable mysql
This ensures that after a server reboot — which inevitably happens during patching windows — my database comes back online without manual intervention.
Understanding the Configuration File
On Ubuntu, MySQL’s main configuration file lives at /etc/mysql/mysql.conf.d/mysqld.cnf. I always review a handful of settings right after installation:
[mysqld]
bind-address = 127.0.0.1
port = 3306
datadir = /var/lib/mysql
innodb_buffer_pool_size = 1G
max_connections = 151
bind-addresscontrols which network interface MySQL listens on. I leave this as127.0.0.1unless I explicitly need remote access.innodb_buffer_pool_sizeis the single most important tuning parameter for InnoDB performance — I usually set it to 60–70% of available RAM on a dedicated database server.
Storage Engines: What I Choose and Why
| Storage Engine | Transactions | Row-Level Locking | Best Use Case |
|---|---|---|---|
| InnoDB | Yes | Yes | Default choice for almost everything |
| MyISAM | No | Table-level only | Legacy read-heavy, non-transactional workloads |
| Memory | No | Table-level | Temporary, in-RAM lookup tables |
| Archive | No | N/A | Historical, append-only log data |
I default to InnoDB unless I have a very specific reason to use something else, because ACID compliance and crash recovery matter more to me than the marginal speed gains of MyISAM.
A Real-World Scenario: Setting Up a Fresh App Server
When I provisioned a new Ubuntu 22.04 server for a client’s e-commerce backend, my exact workflow was:
- Update packages and install MySQL Server.
- Run
mysql_secure_installation. - Create an application-specific database and a least-privilege user scoped to that database only.
- Restrict
bind-addressto the private network interface. - Enable the UFW firewall and only allow port 3306 from the application server’s IP.
sudo ufw allow from 10.0.0.5 to any port 3306
This kind of least-privilege setup is something I insist on for every client project, because an exposed MySQL port on the public internet is one of the most common breach vectors I’ve seen.
Security Best Practices I Always Follow
- I never expose port 3306 to the public internet without a VPN or SSH tunnel.
- I always enforce SSL/TLS for remote connections.
- I create per-application database users instead of sharing root credentials.
- I rotate passwords regularly and store them in a secrets manager, never in plaintext config files.
- I enable the
general_logonly temporarily for debugging, since it has a real performance cost.
Troubleshooting Common Installation Issues
Issue: MySQL service fails to start
sudo journalctl -u mysql.service -n 50
I check this log first — it almost always tells me whether it’s a permissions issue on /var/lib/mysql or a corrupted configuration file.
Issue: “Access denied for user ‘root’@’localhost'”
This usually means the auth_socket plugin is active. I switch the authentication method with:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'NewPassword123!';
FLUSH PRIVILEGES;
Issue: Port 3306 already in use
sudo lsof -i :3306
I use this to identify and stop whatever process is conflicting, often a leftover MariaDB instance.
Performance Considerations Right After Installation
Even before I load any real data, I set a baseline by checking key InnoDB status variables:
SHOW ENGINE INNODB STATUS\G
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
I also make sure the data directory sits on fast storage (SSD/NVMe), since disk I/O is almost always the first bottleneck I encounter in production MySQL deployments.
Frequently Asked Questions
Q: Can I install multiple versions of MySQL on the same Ubuntu server? A: Technically yes, using different data directories and ports, but I avoid this in production. I prefer Docker containers when I need multiple isolated MySQL versions.
Q: Do I need to install MySQL Workbench separately? A: Yes, mysql-server only installs the database engine. I install MySQL Workbench separately, or I use the command-line client and tools like DBeaver.
Q: What’s the difference between MySQL and MariaDB on Ubuntu? A: MariaDB is a community fork of MySQL. I choose based on project requirements — MySQL for Oracle-backed enterprise features, MariaDB when I want a fully open-source stack.
Q: How do I completely uninstall MySQL if something goes wrong? A:
sudo apt purge mysql-server mysql-client mysql-common -y
sudo rm -rf /etc/mysql /var/lib/mysql
sudo apt autoremove -y
Interview Questions I’ve Been Asked (and Asked Others)
- What are the main storage engines in MySQL, and how do they differ?
- Explain the difference between
mysql_secure_installationsteps and why each matters. - How does MySQL’s connection layer handle authentication?
- What is
innodb_buffer_pool_size, and how would you tune it on a server with 16 GB of RAM? - How would you troubleshoot MySQL failing to start after a server reboot?
- What’s the difference between binding MySQL to
127.0.0.1versus0.0.0.0?
Summary and Key Takeaways
Installing MySQL on Ubuntu is straightforward on the surface, but I’ve learned that the real value comes from understanding the architecture underneath — the connection layer, SQL layer, and storage engines — and from following disciplined security practices from day one. I always secure the installation immediately, create scoped users instead of relying on root, and tune innodb_buffer_pool_size before putting any real workload on the server.
Key takeaways:
- Use
apt install mysql-serverfor the standard installation path, or Oracle’s APT repo for the latest version. - Always run
mysql_secure_installationimmediately after installing. - Understand the layered architecture (connection, SQL, storage engine) to troubleshoot effectively.
- InnoDB is my default storage engine for transactional integrity.
- Secure the network layer with firewalls and SSL/TLS before going to production.
References
- MySQL Official Documentation: https://dev.mysql.com/doc/
- MySQL 8.0 Reference Manual, Installation Chapter: https://dev.mysql.com/doc/refman/8.0/en/installing.html
- Ubuntu Server Documentation: https://ubuntu.com/server/docs