In this tutorial, you’ll install MariaDB 10.11 on Ubuntu 24.04 from Ubuntu’s default repository, verify secure unix_socket administration, run the MariaDB hardening utility, create a dedicated application database and user, complete an authenticated CRUD test, and keep port 3306 private.
MariaDB is an open-source relational database that began as a fork of MySQL. It remains compatible with many MySQL clients and applications, but the two database systems have diverged and should not be treated as identical. Ubuntu 24.04 provides the MariaDB 10.11 long-term support series through APT.
Raff supports 3,000+ customers and 15,000+ VMs in its us-east region. Raff Linux VMs provide KVM virtualization, full root access, NVMe storage, and 3 Gbps unmetered bandwidth for self-hosted database workloads.
Prerequisites:
- A Raff Linux VM running Ubuntu 24.04
- SSH access with a user that has sudo privileges
- At least 2 GB RAM for development or light workloads; 4 GB or more is a better production starting point
- A backup plan before modifying an existing MariaDB installation
- Port
22/tcpavailable for SSH administration
📌 Local-first design: This tutorial keeps MariaDB bound to the loopback interface. Applications on the same VM can connect locally, while port
3306remains unavailable from the public internet.
The original workflow was tested on a Raff VM with 2 vCPU and 4 GB RAM. The package, authentication, account, and network guidance was reviewed against current MariaDB documentation in July 2026.
Step 1 — Update Ubuntu and install MariaDB
Update the package index and install current system updates:
sudo apt update sudo apt upgrade -y
Install MariaDB Server, the command-line client, OpenSSL, and UFW from Ubuntu’s repositories:
sudo apt install -y mariadb-server mariadb-client openssl ufw
MariaDB documents package-manager installation in its MariaDB Server installation guide.
Verify the installed client and server versions:
mariadb --version sudo mariadb -NBe 'SELECT VERSION();'
Expected output follows this format:
mariadb Ver 15.1 Distrib 10.11.x-MariaDB 10.11.x-MariaDB-0ubuntu0.24.04.x
The exact patch revision changes as Ubuntu publishes maintenance and security updates.
Step 2 — Verify the MariaDB service
MariaDB should start automatically after installation. Confirm that the service is active:
systemctl is-active mariadb
Expected output:
active
Confirm that MariaDB starts at boot:
systemctl is-enabled mariadb
Expected output:
enabled
View the full service state without opening an interactive pager:
sudo systemctl status mariadb --no-pager
Expected output includes:
Active: active (running)
Verify that the server responds through its local Unix socket:
sudo mariadb-admin ping
Expected output:
mysqld is alive
Check the active TCP listener:
sudo ss -lntp | grep ':3306'
Expected output includes the loopback address:
127.0.0.1:3306
Step 3 — Verify root socket authentication
Ubuntu’s MariaDB packages configure the administrative root@localhost account to use the unix_socket authentication plugin. MariaDB verifies the local Linux root identity through the Unix socket, so administrative access uses sudo mariadb instead of a separate MariaDB root password.
Open the MariaDB client:
sudo mariadb
Expected prompt:
MariaDB [(none)]>
Verify the root account’s host and authentication plugin:
SELECT user, host, plugin FROM mysql.user WHERE user = 'root';
Expected output includes:
root | localhost | unix_socket
Exit the client:
EXIT;
Keep root@localhost on unix_socket unless you have a documented operational requirement to change it. Applications must never use the root database account.
⚠️ Important: Do not replace socket authentication merely to connect an application or database administration tool. Create a separate, restricted database account instead.
Step 4 — Run mariadb-secure-installation
MariaDB includes a hardening utility that removes insecure defaults such as anonymous accounts, remote root access, and the test database.
Run it locally with sudo:
sudo mariadb-secure-installation
The exact prompts depend on the Ubuntu package revision and current server state. Recommended outcomes for a new server are:
- Press
Enterwhen asked for the current root password on a fresh socket-authenticated installation - Keep or enable
unix_socketauthentication for the root account - Do not add a root password when socket authentication already protects local administration
- Remove anonymous users:
Y - Disallow root login remotely:
Y - Remove the test database:
Y - Reload privilege tables:
Y
MariaDB notes that many older root-password recommendations no longer apply because unix_socket authentication is enabled by default on modern installations. Review the official mariadb-secure-installation documentation for the current utility behavior.
Verify that no anonymous accounts remain:
sudo mariadb -NBe \ "SELECT user, host FROM mysql.user WHERE user = '';"
The command should return no rows.
Verify that the test database is absent:
sudo mariadb -NBe \ "SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'test';"
The command should return no rows.
Verify that root still uses socket authentication:
sudo mariadb -NBe \ "SELECT user, host, plugin FROM mysql.user WHERE user = 'root';"
Expected output includes:
root localhost unix_socket
Step 5 — Create an application database and user
Create a separate database account for the application. The account will receive privileges only on its own database rather than global administrative access.
Generate a strong hexadecimal password. The password value is not written into your shell history:
DB_PASSWORD="$(openssl rand -hex 24)" printf 'Store this MariaDB password securely: %s\n' "$DB_PASSWORD"
Copy the generated password into your secrets manager or another protected credential store before continuing.
Create the database and local application user:
sudo mariadb <<SQL CREATE DATABASE raffapp CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'raffappuser'@'localhost' IDENTIFIED BY '${DB_PASSWORD}'; GRANT ALL PRIVILEGES ON raffapp.* TO 'raffappuser'@'localhost'; SQL
Remove the password from the current shell variable after storing it safely:
unset DB_PASSWORD
Verify the database, account, authentication plugin, and grants:
sudo mariadb -NBe \ "SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'raffapp';" sudo mariadb -NBe \ "SELECT user, host, plugin FROM mysql.user WHERE user = 'raffappuser';" sudo mariadb -e \ "SHOW GRANTS FOR 'raffappuser'@'localhost';"
Expected output includes:
raffapp raffappuser localhost mysql_native_password GRANT ALL PRIVILEGES ON `raffapp`.* TO `raffappuser`@`localhost`
CREATE USER and GRANT take effect immediately. A manual FLUSH PRIVILEGES is not required when accounts and privileges are changed with these SQL statements.
📌 Privilege rule:
ALL PRIVILEGES ON raffapp.*is limited to one database. For applications with separate migration and runtime accounts, grant onlySELECT,INSERT,UPDATE, andDELETEto the runtime account.
Step 6 — Verify the application login
Connect to the new database as the application user:
mariadb -u raffappuser -p raffapp
Enter the generated application password when prompted. The password is not displayed while you type.
Verify the authenticated identity and selected database:
SELECT CURRENT_USER(), DATABASE();
Expected output includes:
raffappuser@localhost | raffapp
Confirm that the account cannot access unrelated application databases:
SHOW DATABASES;
The account can see system schemas required by MariaDB and the raffapp database, but it should not have privileges on other application databases.
Exit the client:
EXIT;
Step 7 — Run an authenticated CRUD test
Connect as the application user:
mariadb -u raffappuser -p raffapp
Create a temporary table:
CREATE TABLE tutorial_check ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, name VARCHAR(100) NOT NULL, status VARCHAR(32) NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY unique_name (name) ) ENGINE=InnoDB;
Insert a row:
INSERT INTO tutorial_check (name, status) VALUES ('raff-mariadb-test', 'created');
Read it:
SELECT name, status FROM tutorial_check WHERE name = 'raff-mariadb-test';
Expected output:
raff-mariadb-test | created
Update the row:
UPDATE tutorial_check SET status = 'verified' WHERE name = 'raff-mariadb-test';
Verify the update:
SELECT name, status FROM tutorial_check WHERE name = 'raff-mariadb-test';
Expected output:
raff-mariadb-test | verified
Delete the row and remove the temporary table:
DELETE FROM tutorial_check WHERE name = 'raff-mariadb-test'; DROP TABLE tutorial_check;
Exit the client:
EXIT;
The database is working end to end when the application user can authenticate and create, read, update, and delete data in raffapp.
Step 8 — Keep MariaDB local and protect port 3306
Verify the configured bind address and port:
sudo mariadb -NBe \ "SELECT @@bind_address, @@port;"
Expected output:
127.0.0.1 3306
Confirm the active socket:
sudo ss -lntp | grep ':3306'
Expected output includes only the loopback address:
127.0.0.1:3306
Allow SSH before enabling UFW so you do not lock yourself out:
sudo ufw allow OpenSSH
If the OpenSSH profile is unavailable, allow the SSH port directly:
sudo ufw allow 22/tcp
Add a deny rule for MariaDB as defense in depth and enable UFW:
sudo ufw deny 3306/tcp sudo ufw --force enable
Verify the firewall rules:
sudo ufw status numbered
Expected output includes:
OpenSSH ALLOW IN 3306/tcp DENY IN
📌 Important: The UFW rule does not replace
bind-address = 127.0.0.1. The bind address prevents MariaDB from listening on public interfaces; the firewall supplies a second control.
For an application and database running on the same VM, no inbound MariaDB rule is required.
Step 9 — Optional: allow one private application server
Use this section only when the application runs on a separate VM connected through a private network. Do not expose MariaDB to the public internet.
Identify the database VM’s private IP and the application VM’s private IP. The examples below use:
Database private IP: 10.0.0.5 Application private IP: 10.0.0.10
Back up the MariaDB configuration:
sudo cp /etc/mysql/mariadb.conf.d/50-server.cnf \ /etc/mysql/mariadb.conf.d/50-server.cnf.before-private-network
Edit the server configuration:
sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf
Find the existing setting:
bind-address = 127.0.0.1
Change it to the database VM’s specific private IP:
bind-address = 10.0.0.5
MariaDB continues to support local administrative access through the Unix socket. Applications that previously connected over 127.0.0.1 must use the local socket or the approved private address after this change.
Do not use 0.0.0.0 when a specific private interface is available.
Restart MariaDB and verify the service:
sudo systemctl restart mariadb systemctl is-active mariadb sudo ss -lntp | grep ':3306'
Expected output includes:
10.0.0.5:3306
Generate a separate password for the private-network account:
REMOTE_DB_PASSWORD="$(openssl rand -hex 24)" printf 'Store this remote MariaDB password securely: %s\n' "$REMOTE_DB_PASSWORD"
Create an account restricted to the application VM’s exact private IP:
sudo mariadb <<SQL CREATE USER 'raffappuser'@'10.0.0.10' IDENTIFIED BY '${REMOTE_DB_PASSWORD}'; GRANT ALL PRIVILEGES ON raffapp.* TO 'raffappuser'@'10.0.0.10'; SQL unset REMOTE_DB_PASSWORD
Verify the remote account and grants:
sudo mariadb -NBe \ "SELECT user, host, plugin FROM mysql.user WHERE user = 'raffappuser';" sudo mariadb -e \ "SHOW GRANTS FOR 'raffappuser'@'10.0.0.10';"
Remove the broad deny rule and allow only the application VM through UFW:
sudo ufw delete deny 3306/tcp sudo ufw allow from 10.0.0.10 to 10.0.0.5 port 3306 proto tcp sudo ufw status numbered
From the application VM, install the MariaDB client if necessary and test the private connection:
sudo apt update sudo apt install -y mariadb-client mariadb \ --host=10.0.0.5 \ --user=raffappuser \ --password \ raffapp
After verification, confirm that port 3306 remains unavailable through the database VM’s public IP. Review public vs private traffic before separating application and database servers.
For traffic that leaves a trusted private network, configure MariaDB TLS or use a VPN. A public firewall rule alone does not provide transport encryption.
MariaDB’s remote client access guide explains how bind-address, account hosts, and firewall rules work together.
Step 10 — Run the final verification
Run the final service, account, network, and firewall checks:
echo "MariaDB service:" systemctl is-active mariadb echo "Boot status:" systemctl is-enabled mariadb echo "Version:" sudo mariadb -NBe 'SELECT VERSION();' echo "Root authentication:" sudo mariadb -NBe \ "SELECT user, host, plugin FROM mysql.user WHERE user = 'root';" echo "Application account:" sudo mariadb -NBe \ "SELECT user, host, plugin FROM mysql.user WHERE user = 'raffappuser';" echo "Listening socket:" sudo ss -lntp | grep ':3306' echo "Firewall:" sudo ufw status numbered
The installation is complete when:
- MariaDB 10.11 is installed and the service is active and enabled
root@localhostusesunix_socket- Anonymous users and the test database are absent
raffappuserhas privileges only onraffapp- The application user can complete authenticated CRUD operations
- MariaDB listens only on loopback, or on one approved private IP
- UFW blocks public access to port
3306