Guides

Install MariaDB or MySQL on Ubuntu 24.04 and Debian 12/13 — and secure it

Install MariaDB (Debian's default) or MySQL 8 on Ubuntu 24.04 and Debian 12/13, harden it, create an app user and allow remote access safely.

Published ~9 min read

Short answer: Debian ships MariaDB, not Oracle MySQL, so on Debian 12 and 13 you run sudo apt install mariadb-server; MySQL there needs Oracle’s own APT repository. Ubuntu 24.04 has both in its archive: mysql-server (MySQL 8.0) and mariadb-server (MariaDB 10.11). Rocky Linux 9 has both in AppStream. After installing, run the secure-installation script, leave root on socket login, and give every application its own database user.

the short version
# Debian 12 · 13, or Ubuntu: MariaDB
sudo apt update
sudo apt install -y mariadb-server
sudo mariadb-secure-installation

# Ubuntu 24.04: MySQL 8.0 instead
sudo apt install -y mysql-server
sudo mysql_secure_installation

# then, as root on the server
sudo mariadb        # or: sudo mysql

MariaDB or MySQL — which should I install?

MariaDB began in 2009 as a fork of MySQL by MySQL’s original developers. Both speak the same client protocol and nearly the same SQL, so the mysql command line, PHP’s mysqli and PDO drivers, WordPress, Nextcloud and phpMyAdmin work with either. What each distribution ships:

  • Debian 12: MariaDB 10.11. Debian 13: MariaDB 11.8. No Oracle MySQL package.
  • Ubuntu 24.04: MySQL 8.0 or MariaDB 10.11, your choice.
  • Rocky Linux 9: MariaDB 10.5 (10.11 as a module stream) or MySQL 8.0.

Without a specific requirement, take what your distribution prefers: MariaDB on Debian, either on Ubuntu. Choose MySQL when an application names MySQL 8 as a requirement, or when you replicate from or migrate off an existing MySQL 8 server. The two have drifted apart over the years: JSON handling, default authentication plugins and some newer SQL features differ, and their data directories are not interchangeable. Switching later means a dump and a restore, not swapping packages.

How do I install MySQL or MariaDB on Ubuntu 24.04?

Ubuntu 24.04
sudo apt update

# MySQL 8.0
sudo apt install -y mysql-server
systemctl status mysql

# or MariaDB 10.11
sudo apt install -y mariadb-server
systemctl status mariadb

Pick one: the two packages conflict and cannot run side by side. The server starts at once and is enabled at boot, listening on 127.0.0.1:3306 only.

How do I install MariaDB or MySQL on Debian 12 and 13?

Debian 12 · 13
sudo apt update
sudo apt install -y mariadb-server
mariadb --version

sudo apt install mysql-server on Debian ends with Package 'mysql-server' has no installation candidate. The default-mysql-server package exists, but it simply pulls in MariaDB. If you really need Oracle MySQL, add Oracle’s repository:

Oracle MySQL on Debian
# take the current file name from https://dev.mysql.com/downloads/repo/apt/
wget https://dev.mysql.com/get/mysql-apt-config_0.8.XX-1_all.deb
sudo apt install ./mysql-apt-config_0.8.XX-1_all.deb   # choose the series, e.g. 8.4 LTS
sudo apt update
sudo apt install -y mysql-server                        # asks for a root password

Check on the download page that your Debian release is supported before you start. If apt update later complains about an expired or missing key for that repository, see GPG error: NO_PUBKEY.

How do I install MariaDB or MySQL on Rocky Linux 9?

Rocky Linux 9 · AlmaLinux 9
# MariaDB 10.5
sudo dnf install -y mariadb-server
# or MariaDB 10.11 instead:  sudo dnf module install -y mariadb:10.11
sudo systemctl enable --now mariadb
sudo mariadb-secure-installation

# or MySQL 8.0
sudo dnf install -y mysql-server
sudo systemctl enable --now mysqld
sudo mysql_secure_installation

How do I secure MariaDB or MySQL after installing?

Run the script that matches your server. It removes anonymous accounts, the test database and remote root logins:

secure installation
sudo mariadb-secure-installation     # MariaDB
sudo mysql_secure_installation       # MySQL
  • Enter current password for root (MariaDB): just press Enter.
  • Switch to unix_socket authentication (MariaDB): on Debian and Ubuntu it is already active, and the script tells you that you can safely answer n.
  • Change the root password: not needed. With socket authentication the password is never checked. Ubuntu’s MySQL skips this step for the same reason.
  • VALIDATE PASSWORD component (MySQL): optional. It enforces strong passwords for every account you create later.
  • Remove anonymous users, disallow remote root login, remove the test database, reload privileges: yes to all.

On Debian and Ubuntu, the database root account is tied to the operating system’s root user through the Unix socket. sudo mariadb or sudo mysql logs you in without a password, while mysql -u root -p with a password is refused. That is by design, not a fault; the details are in Access denied for user ‘root’@‘localhost’. Leave root like this and use separate accounts for applications.

How do I create a database and a user?

sudo mariadb (or sudo mysql)
CREATE DATABASE app CHARACTER SET utf8mb4;
CREATE USER 'app'@'localhost' IDENTIFIED BY 'a-long-random-password';
GRANT ALL PRIVILEGES ON app.* TO 'app'@'localhost';
SHOW GRANTS FOR 'app'@'localhost';
EXIT;

Generate the password with openssl rand -base64 24, then test the login as the new user with mariadb -u app -p app (mysql -u app -p app on MySQL). GRANT, CREATE USER and ALTER USER take effect at once, so the FLUSH PRIVILEGES many guides add is unnecessary. It is only needed after editing the grant tables directly with INSERT or UPDATE, and it does no harm.

ALL PRIVILEGES ON app.* covers one database only, which is what most applications need, because they create their own tables on install and update. A reporting tool that only reads gets GRANT SELECT ON app.* instead.

How do I allow remote connections safely?

The safest remote connection is one that never opens port 3306. An SSH tunnel carries the database connection inside your SSH session:

on your computer
ssh -N -L 3307:127.0.0.1:3306 you@203.0.113.10

# in a second terminal, or point your GUI tool at 127.0.0.1:3307
mysql -h 127.0.0.1 -P 3307 -u app -p app

DBeaver, HeidiSQL, MySQL Workbench and TablePlus have this built in as an “SSH tunnel” option. Through the tunnel the server sees a connection from 127.0.0.1; if it rejects 'app'@'localhost', create the same user for 'app'@'127.0.0.1'.

If an application server elsewhere has to connect directly, open the database to that one address:

  1. 1Listen on the public interface
    bind-address · under [mysqld]
    # MariaDB on Debian/Ubuntu: /etc/mysql/mariadb.conf.d/50-server.cnf
    # MySQL on Ubuntu:          /etc/mysql/mysql.conf.d/mysqld.cnf
    # Rocky:                    a file in /etc/my.cnf.d/
    bind-address = 0.0.0.0
    apply
    sudo systemctl restart mariadb      # or: mysql (Ubuntu), mysqld (Rocky)
  2. 2Create the user for that address
    sudo mariadb
    CREATE USER 'app'@'198.51.100.7' IDENTIFIED BY 'a-long-random-password';
    GRANT ALL PRIVILEGES ON app.* TO 'app'@'198.51.100.7';
  3. 3Allow only that address through the firewall
    ufw · firewalld
    # Ubuntu, Debian
    sudo ufw allow from 198.51.100.7 to any port 3306 proto tcp
    
    # Rocky
    sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="198.51.100.7/32" port port="3306" protocol="tcp" accept'
    sudo firewall-cmd --reload
  4. 4Test from the application server
    on 198.51.100.7
    mysql -h 203.0.113.10 -u app -p app

    Can't connect to … server means the port is not reachable: check bind-address and the firewall. Host … is not allowed to connect means you got through but no user exists for that address.

How do I keep it from eating the RAM of a small VPS?

The biggest lever is innodb_buffer_pool_size, the cache for table data and indexes. It defaults to 128 MB. A dedicated database server gives it half or more of its RAM; on a VPS that also runs a web server and PHP, keep it moderate, for example 512 MB to 1 GB on a 4 GB machine. Put your settings in a file of your own, so package updates leave them alone:

/etc/mysql/mariadb.conf.d/99-tuning.cnf
# MySQL on Ubuntu: /etc/mysql/mysql.conf.d/99-tuning.cnf
# Rocky:            /etc/my.cnf.d/99-tuning.cnf
[mysqld]
innodb_buffer_pool_size = 512M
max_connections         = 50
# MySQL 8 only (MariaDB has it off already): saves noticeable memory
performance_schema      = OFF
apply and check
sudo systemctl restart mariadb      # or: mysql / mysqld
sudo mariadb -e "SELECT @@innodb_buffer_pool_size/1024/1024 AS buffer_pool_mb, @@max_connections;"
# MySQL: the same with sudo mysql -e

If the kernel has already killed the database once, the log shows Out of memory: Killed process … (mariadbd) or (mysqld). How to read and prevent that is in Killed: why Linux kills your process when memory runs out.

How do I back up MariaDB or MySQL?

dump and restore
# one database, consistent without locking InnoDB tables
sudo mariadb-dump --single-transaction --routines --events app | gzip > app-$(date +%F).sql.gz
# MySQL: the same options with mysqldump

# all databases, the mysql system schema included
sudo mariadb-dump --single-transaction --all-databases | gzip > all-$(date +%F).sql.gz

# restore into an existing, empty database
gunzip < app-2026-11-26.sql.gz | sudo mariadb app

Run it nightly from /etc/cron.d. As root it logs in through the socket, so no password has to be stored anywhere. In cron, every % must be escaped as \%, or the line is cut off at that point:

/etc/cron.d/db-backup
30 3 * * * root mariadb-dump --single-transaction --routines --events app | gzip > /var/backups/app-$(date +\%F).sql.gz
15 4 * * * root find /var/backups -name 'app-*.sql.gz' -mtime +14 -delete

Copy the files off the server as well; a backup on the same disk does not survive losing the server. If the job never seems to run, see cron job not running.

Do I want a web interface?

For browsing tables and running the odd query, phpMyAdmin is the usual choice, and both distributions package it. The phpMyAdmin guide installs it and, more importantly, keeps it away from the bots that probe every server for /phpmyadmin. If your application expects PostgreSQL instead, see installing PostgreSQL.

A database is only as safe as the disk underneath it. On VMHeaven’s Standard KVM line, VPS disks live on replicated Ceph storage rather than a single drive, which is a good base for MariaDB, though no replacement for the dumps above. With the Ubuntu or Debian image, every command on this page works as written.

Frequently asked

Should I install MariaDB or MySQL?

Without a specific requirement, use what your distribution ships: MariaDB on Debian, which has no Oracle MySQL package, and either on Ubuntu 24.04. Choose MySQL 8 when an application explicitly requires it or when you replicate from or migrate off an existing MySQL 8 server. Most PHP applications, WordPress and phpMyAdmin work with both, but switching later needs a dump and restore.

What is the default MySQL or MariaDB root password on Ubuntu and Debian?

There is none. The root account authenticates through the Unix socket (unix_socket on MariaDB, auth_socket on MySQL), so 'sudo mariadb' or 'sudo mysql' logs you in as the operating system's root user without a password, while 'mysql -u root -p' is refused. Leave it that way and create separate users for applications.

How do I check which MariaDB or MySQL version is installed?

Run 'mariadb --version' or 'mysql --version' for the client, or 'sudo mariadb -e "SELECT VERSION();"' (or 'sudo mysql -e ...') for the running server. Debian 12 ships MariaDB 10.11, Debian 13 MariaDB 11.8, and Ubuntu 24.04 MySQL 8.0 or MariaDB 10.11.

How do I allow remote access to MySQL or MariaDB?

The safest way is an SSH tunnel ('ssh -N -L 3307:127.0.0.1:3306 you@server') so port 3306 stays closed. For a direct connection, set 'bind-address = 0.0.0.0' in 50-server.cnf (MariaDB) or mysqld.cnf (MySQL), create the user for the client's IP ('app'@'198.51.100.7') and allow only that IP in the firewall, e.g. 'ufw allow from 198.51.100.7 to any port 3306 proto tcp'. Never open 3306 to everyone.

Related articles

All guides