Install Apache, PHP-FPM and MariaDB (LAMP) on Ubuntu 24.04
A classic LAMP stack (Linux, Apache, MariaDB, PHP) on Ubuntu 24.04. This version uses PHP-FPM instead of mod_php, which is the modern setup: Apache stays lean, PHP runs as a separate service, and each site can later get its own PHP pool and user.
1. Install the packages
sudo apt update
sudo apt install -y apache2 mariadb-server php-fpm php-mysql php-mbstring php-xml php-curl php-zip php-gd unzip curl
Ubuntu 24.04 ships PHP 8.3 and MariaDB 10.11.
2. Connect Apache to PHP-FPM
sudo a2enmod proxy_fcgi setenvif rewrite headers
sudo a2enconf php8.3-fpm
sudo systemctl restart apache2
Check that everything is running:
systemctl is-active apache2 mariadb php8.3-fpm
3. Secure MariaDB
sudo mariadb-secure-installation
Answer Y to removing anonymous users and the test database, and to disallowing remote root login. On Ubuntu the database root account uses the unix socket, so sudo mariadb logs you in without a password, which is safer than a password.
Create a database and a dedicated user for each app, never use root in application code:
CREATE DATABASE myapp CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'myapp'@'localhost' IDENTIFIED BY 'a-long-random-password';
GRANT ALL PRIVILEGES ON myapp.* TO 'myapp'@'localhost';
FLUSH PRIVILEGES;
Generate the password with openssl rand -hex 16. MariaDB listens on 127.0.0.1 only by default, and you should keep it that way.
4. Create a virtual host
Put each site in its own folder, with a public/ directory as the web root so config and source code can't be downloaded:
sudo mkdir -p /var/www/myapp/public
echo '<?php echo "PHP " . PHP_VERSION;' | sudo tee /var/www/myapp/public/index.php
# /etc/apache2/sites-available/myapp.conf
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/myapp/public
<Directory /var/www/myapp/public>
AllowOverride All
Require all granted
</Directory>
<FilesMatch "\.php$">
SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost"
</FilesMatch>
ErrorLog ${APACHE_LOG_DIR}/myapp-error.log
CustomLog ${APACHE_LOG_DIR}/myapp-access.log combined
</VirtualHost>
sudo a2ensite myapp
sudo a2dissite 000-default
sudo apache2ctl configtest && sudo systemctl reload apache2
Test: curl -H 'Host: example.com' http://127.0.0.1/ should print the PHP version.
5. Permissions
Files owned by root, readable by the web server group, and not world-readable:
sudo chown -R root:www-data /var/www/myapp
sudo chmod -R g+rX,o-rwx /var/www/myapp
Only directories the app must write to (uploads, cache) should be group-writable. Never chmod 777.
6. Firewall and HTTPS
sudo ufw allow OpenSSH && sudo ufw allow 80,443/tcp && sudo ufw enable
sudo apt install -y certbot python3-certbot-apache
sudo certbot --apache -d example.com -d www.example.com
Your DNS must already point at the server for Let's Encrypt to validate the domain. Certbot installs a timer that renews certificates automatically.
7. Sensible PHP settings
Edit /etc/php/8.3/fpm/php.ini (and restart php8.3-fpm):
expose_php = Off
display_errors = Off
log_errors = On
upload_max_filesize = 16M
post_max_size = 16M
memory_limit = 256M
Troubleshooting
| Problem | Check |
|---|---|
| PHP source shown as text | a2enconf php8.3-fpm and the SetHandler block are missing |
| 503 / "proxy: error" | systemctl status php8.3-fpm, socket path in SetHandler |
| 403 Forbidden | Require all granted and directory permissions |
| 404 on every pretty URL | a2enmod rewrite, AllowOverride All, .htaccess present |
| Can't connect to DB | Correct user/host/password; sudo mariadb -e "SHOW GRANTS FOR 'myapp'@'localhost'" |
| Check the logs | /var/log/apache2/myapp-error.log and journalctl -u php8.3-fpm |