What you will achieve
Apache, MariaDB, and PHP on Ubuntu — a classic LAMP stack for WordPress, internal tools, or PHP APIs behind UFW.
1) Install packages
sudo apt update
sudo apt install apache2 mariadb-server php libapache2-mod-php php-mysql
2) Secure MariaDB
sudo mysql_secure_installation
3) Test PHP
echo '<?php phpinfo(); ?>' | sudo tee /var/www/html/info.php
curl -s localhost/info.php | head
sudo rm /var/www/html/info.php
Remove info.php immediately — it exposes server details.
4) Firewall
sudo ufw allow 'Apache Full'
sudo ufw status
Verify
systemctl is-active apache2 mariadb
php -v
5) PHP-FPM alternative (modern)
sudo apt install php-fpm
sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php8.3-fpm
Separates PHP from Apache mod_php — better isolation and resource limits.
6) MariaDB create app database
sudo mysql -e "CREATE DATABASE myapp; CREATE USER 'myapp'@'localhost' IDENTIFIED BY 'strongpass'; GRANT ALL ON myapp.* TO 'myapp'@'localhost'; FLUSH PRIVILEGES;"
7) Virtual host
sudo a2ensite myapp.conf
sudo a2dissite 000-default.conf
sudo systemctl reload apache2
Security basics
Disable directory listing, keep PHP updated, use .htaccess or vhost config to block access to .git and upload dirs. Fail2ban on Apache auth failures optional.
8) mod_security optional WAF
sudo apt install libapache2-mod-security2
Prerequisites
Ubuntu 22.04/24.04 LTS, sudo, ports 80/443 available, domain optional for vhost. Basic SQL knowledge for MariaDB user creation. UFW awareness if enabled.
PHP version selection
apt-cache policy php
Ubuntu ships multiple PHP versions via ppa:ondrej/php if stock version too old for your app — pin version explicitly in apt install.
a2query enabled modules
a2query -m
sudo a2enmod rewrite headers sslEnable rewrite for WordPress permalinks — stock install lacks mod_rewrite enabled.
PHP opcache production
sudo phpenmod opcache
grep opcache /etc/php/8.3/apache2/php.iniEnable opcache for production PHP — disable opcache.validate_timestamps in prod after deploy workflow uses reload not file edits.
mysql vs mariadb socket auth
Default ubuntu user socket plugin — apps need dedicated SQL user with password from localhost not sudo mysql root shortcut in production app configs.
fail2ban apache jails
sudo apt install fail2ban
sudo systemctl enable fail2banEnable apache-auth jail after LAMP go-live — reduces noise in access.log from credential stuffing.
apache2ctl configtest
sudo apache2ctl configtest
sudo systemctl reload apache2After any vhost edit — syntax error takes down entire apache on reload if not tested first.
ufw allow 443 after certbot
Certbot standalone needs 80 during issue — production runs nginx plugin on 443 after — verify both ports in ufw status numbered.