Ubuntu 22.04 Server / How to Install Apache, PHP, and MariaDB
On Ubuntu 22.04 Server, install Apache, its PHP module, MariaDB, and the PHP database extension to build a basic web runtime. After installation, separate the MariaDB administrator account from the web application account and check that PHP can read a value from the database.
Installation scope and packages
This guide runs Apache and MariaDB on one new server using the default Ubuntu 22.04 repositories. You need sudo privileges and an internet connection for package downloads. On an existing server, back up web configuration and databases first, and check for files, accounts, or databases with the example names. If you already use PHP-FPM, do not mix this Apache module configuration into it.
- Update the package index and install the components.
libapache2-mod-phphandles PHP in Apache, whilephp-mysqlsupplies extensions for MariaDB access. Also installphp-clifor command line checks. See the Ubuntu PHP installation guide for this configuration.
sudo apt update
sudo apt install apache2 mariadb-server php php-cli libapache2-mod-php php-mysql
- Start the services and enable them at boot. Check that the Apache configuration test reports
Syntax OK, then restart Apache to load the PHP module.
sudo systemctl enable --now apache2 mariadb
sudo apache2ctl configtest
sudo systemctl restart apache2
systemctl is-active apache2 mariadb
The last command prints active for each running service. Check installed versions with apache2 -v, php -v, and mariadb --version. Servers with extra repositories may install a different PHP version from the default repositories.
Check MariaDB administrator authentication
The default Ubuntu MariaDB packages generally use Unix socket authentication for local administration. This checks the operating system user identity; it does not mean anyone can connect with an empty root password. Open an administrator session below and inspect the current account and configuration.
sudo mariadb --protocol=socket
SELECT VERSION(), CURRENT_USER();
SHOW CREATE USER 'root'@'localhost';
EXIT;
Root authentication may differ on an existing installation. This example retains socket authentication and creates a separate application account, as explained in MariaDB authentication since version 10.4. Do not directly UPDATE the plugin value in mysql.user or use root in a web application.
Use the following tool to review initial security settings. Prompt wording and order depend on the version and current configuration, so do not automatically answer Y to every question.
sudo mariadb-secure-installation
- If you already administer MariaDB through socket authentication, you can keep it. The official MariaDB tool documentation explains that setting a new root password is usually unnecessary in this configuration.
- On a new server, removing anonymous users and the unneeded test database and restricting remote root login are appropriate choices. For an existing service, check whether these are in use before deciding. Apply the changes when prompted to reload privilege tables.
Create a database and account for the application
Connect again with sudo mariadb --protocol=socket and run the SQL below. lampdemo and lamp_app are new names for this exercise. If they already exist, choose different names and use them consistently throughout the examples; do not overwrite or delete existing objects. Replace the password placeholder with a long, unique password and use the same value in the PHP configuration. A password containing a single quote must follow the respective SQL and PHP string escaping rules.
CREATE DATABASE lampdemo CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'lamp_app'@'localhost' IDENTIFIED BY 'REPLACE_WITH_A_LONG_UNIQUE_PASSWORD';
GRANT SELECT, INSERT, UPDATE, DELETE ON lampdemo.* TO 'lamp_app'@'localhost';
CREATE TABLE lampdemo.health_check (
id INT PRIMARY KEY,
message VARCHAR(100) NOT NULL
);
INSERT INTO lampdemo.health_check (id, message) VALUES (1, 'database-ok');
EXIT;
The administrator creates the table. The application receives only select, insert, update, and delete permissions on this database. If an application installer needs table creation privileges, review those separately. After granting permissions with GRANT, a separate FLUSH PRIVILEGES is unnecessary.
Query from the terminal using the application account and check for database-ok. Do not append the password to -p; enter it at the prompt.
mariadb --protocol=socket -u lamp_app -p lampdemo -e "SELECT message FROM health_check WHERE id=1;"
Create the PHP integration example files
The web document directory of the default Ubuntu Apache site is normally /var/www/html. For an existing virtual host, inspect sudo apache2ctl -S and its DocumentRoot setting and adjust the example web file path. Use another name if a file below already exists. Keep the configuration containing the password outside the web document directory.
- Create the configuration directory and open an editor. These permissions assume Apache runs as the default Ubuntu user and group,
www-data.
sudo install -d -o root -g www-data -m 0750 /etc/lamp-demo
sudoedit /etc/lamp-demo/database.php
Save the entire following content in /etc/lamp-demo/database.php. Replace password with the application password set earlier. As the PHP PDO_MYSQL connection documentation explains, host=localhost uses a socket on Unix. Changing it to 127.0.0.1 selects TCP and can change account matching and connection requirements.
<?php
return [
'dsn' => 'mysql:host=localhost;dbname=lampdemo;charset=utf8mb4',
'user' => 'lamp_app',
'password' => 'REPLACE_WITH_A_LONG_UNIQUE_PASSWORD',
];
- Keep the configuration owned by root, readable only by its owner and the Apache group. Then open the web check file.
sudo chown root:www-data /etc/lamp-demo/database.php
sudo chmod 0640 /etc/lamp-demo/database.php
sudoedit /var/www/html/lamp-check.php
Save the entire following content in /var/www/html/lamp-check.php. PDO (PHP Data Objects) is an interface for accessing databases from PHP. This code checks the actual table value before printing success. On failure it returns HTTP 500 with a generic message and writes the detailed cause to the server log.
<?php
declare(strict_types=1);
header('Content-Type: text/plain; charset=UTF-8');
try {
$config = require '/etc/lamp-demo/database.php';
$pdo = new PDO($config['dsn'], $config['user'], $config['password'], [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
$message = $pdo->query('SELECT message FROM health_check WHERE id = 1')->fetchColumn();
if ($message !== 'database-ok') {
throw new RuntimeException('Unexpected database result');
}
echo "PHP OK\nDatabase OK\n";
} catch (Throwable $e) {
http_response_code(500);
error_log('lamp-check failed: ' . $e->getMessage());
echo "Check failed; review server logs.\n";
}
- Give the web check file readable permissions and check both files for PHP syntax errors.
No syntax errors detectedis a syntax check result, not proof of a successful database connection.
sudo chown root:root /var/www/html/lamp-check.php
sudo chmod 0644 /var/www/html/lamp-check.php
sudo php -l /etc/lamp-demo/database.php
php -l /var/www/html/lamp-check.php
The configuration may not be readable by an ordinary user, so its syntax check uses sudo. There is no need to make the entire web directory writable or apply chmod 777.
Check the HTTP response and database connection
Request the following URL from the server itself. Install curl with sudo apt install curl if needed. HTTP status 200 with the following two lines in the response body means Apache ran PHP and PHP read the example row from MariaDB.
curl -i http://127.0.0.1/lamp-check.php
PHP OK
Database OK
From another computer, enter http://SERVER-IP/lamp-check.php in a browser. For a virtual host, use its configured host name. Seeing only the default welcome page or running php -v does not verify this integration.
If access works locally but fails from another computer, check sudo ufw status verbose, cloud security groups, and routing. In a private test network where UFW is already active, the following rule allows HTTP port 80 only from the intended subnet. Replace the example 192.168.10.0/24 with your trusted subnet. Do not disable the firewall or newly enable it without allowing remote SSH access for this test.
sudo ufw allow from 192.168.10.0/24 to any port 80 proto tcp
PHP and MariaDB communicate through a socket on the same server, so there is no need to expose port 3306. Run this HTTP exercise in a trusted environment. Before transmitting login details or other data in production, configure the domain, virtual host, and HTTPS certificate separately.
Troubleshooting and test cleanup
Narrow down connection failures by checking service status, PHP modules, accounts, and file permissions. The commands below inspect Apache configuration and modules and recent logs from both services. Logs can contain operational information; do not post them publicly without reviewing them.
sudo apache2ctl configtest
sudo apache2ctl -M
sudo tail -n 50 /var/log/apache2/error.log
sudo journalctl -u apache2 -u mariadb -n 50 --no-pager
- PHP source is displayed or downloaded: Stop public access and check that the Apache PHP module is enabled and Apache has restarted. If installing
libapache2-mod-phpdoes not resolve it, inspect existing PHP-FPM and virtual host configuration too. - could not find driver: Check that the
php-mysqlextension for the PHP version used by Apache is installed and enabled, then restart Apache. Command line PHP and Apache PHP can use different configurations. - Access denied: Check the application password, the
'lamp_app'@'localhost'account, and its permissions. Do not change root authentication or grant global privileges as a workaround. For a missing socket, compare MariaDB’sSHOW VARIABLES LIKE 'socket';result with the PHP socket configuration. - Permission denied or 404: Check that the Apache user can read the configuration and that the web file is in the requested site’s DocumentRoot. Also inspect directory access permissions for
/etc/lamp-demo.
When finished, delete only the web test file created in this guide with the command below, then check that the same URL no longer returns the success message. Do not leave files exposing environment information, such as an existing phpinfo.php. If a real application uses the configuration, keep it outside the web directory with restricted permissions.
sudo rm -- /var/www/html/lamp-check.php
To remove the exercise data too, first confirm that no other service uses it, then run the SQL below in an administrator session. Dropping a database cannot be undone, so back up anything you need first. Apply this only to the names newly created for this exercise.
DROP DATABASE lampdemo;
DROP USER 'lamp_app'@'localhost';
For a single-server setup, retain MariaDB’s local binding configuration. If remote database access is separately required, do not simply remove bind-address; plan the listening address, allowed sources, dedicated account, and encrypted connection together.



