Install LAMP Stack on Ubuntu 24.04
Install Apache, MariaDB, PHP 8, PHP-FPM, and phpMyAdmin on Ubuntu 24.04 with optional multi-PHP support and a dedicated Apache virtual host.
This guide walks through a full LAMP stack setup on Ubuntu 24.04 using Apache, MariaDB, PHP 8, PHP-FPM, and phpMyAdmin.
By the end, you will have:
- a sudo-enabled admin user
- updated system packages
- SSH on a custom port
- UFW enabled
systemd-resolvedconfigured- Apache, MariaDB, PHP, and phpMyAdmin installed
- an optional multi-PHP setup
- a dedicated PHP-FPM pool and virtual host for a site
Create a Sudo User
Skip this step if you already have a working sudo user.
Create a new user:
adduser newuserGrant sudo access:
sudo usermod -aG sudo newuserSwitch to the new user and verify sudo works:
su - newuser
sudo whoamiIf the command returns root, sudo is configured correctly.
Update Packages
Update package metadata and upgrade installed packages:
sudo apt update && sudo apt upgradeChange the SSH Port
Edit the SSH server config:
sudo vim /etc/ssh/sshd_configSet a custom port and disable root login:
Port 52225
PermitRootLogin noReload the SSH service:
sudo systemctl daemon-reload
sudo systemctl restart sshEnable the Firewall
Install UFW:
sudo apt install ufwAllow HTTP, HTTPS, and your custom SSH port:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 52225/tcpEnable the firewall:
sudo ufw enableCheck active rules:
sudo ufw statusConnect over the new SSH port:
ssh -p 52225 user@your_server_ipEnabling the firewall can drop the current SSH session. Reconnect using the new port if needed.
Configure DNS Resolver
Install and enable systemd-resolved:
sudo apt install systemd-resolved
sudo systemctl enable systemd-resolved
sudo systemctl start systemd-resolved
sudo systemctl status systemd-resolvedEdit the resolver config:
sudo vim /etc/systemd/resolved.confUse Cloudflare and Quad9:
[Resolve]
DNS=1.1.1.1 1.0.0.1 2606:4700:4700::1111 2606:4700:4700::1001
FallbackDNS=9.9.9.9 149.112.112.112 2620:fe::fe 2620:fe::9
DNSOverTLS=opportunisticPoint resolv.conf at the stub resolver and restart:
sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
sudo systemctl restart systemd-resolvedCheck resolver status:
resolvectl statusGenerate an SSL Certificate
Create a self-signed certificate for local testing or internal use:
sudo openssl req -x509 -newkey rsa:2048 -nodes -keyout /etc/ssl/private/default.key -out /etc/ssl/certs/default.crt -days 3650Install PHP 8 and PHP-FPM
Check the PHP packages available in the Ubuntu repositories:
sudo apt search php | egrep '^php[0-9]'Install PHP 8.3 and common extensions:
sudo apt install php8.3 php8.3-cli php8.3-fpm php8.3-common php8.3-mysql php8.3-zip php8.3-gd php8.3-mbstring php8.3-curl php8.3-xml php8.3-bcmath php8.3-gmpThose packages cover common dependencies for WordPress and similar PHP applications.
Optional: Install Multiple PHP Versions
If you want to run multiple PHP versions, add the Ondrej Surý PHP repository:
sudo apt install software-properties-common
sudo add-apt-repository ppa:ondrej/php
sudo apt updateInstall both PHP 7.4 and PHP 8.3:
sudo apt install php7.4 php7.4-cli php7.4-fpm php7.4-common php7.4-mysql php7.4-zip php7.4-gd php7.4-mbstring php7.4-curl php7.4-xml php7.4-bcmath php7.4-gmp
sudo apt install php8.3 php8.3-cli php8.3-fpm php8.3-common php8.3-mysql php8.3-zip php8.3-gd php8.3-mbstring php8.3-curl php8.3-xml php8.3-bcmath php8.3-gmpConfigure PHP-FPM Pools
Edit the PHP 8.3 pool:
sudo vim /etc/php/8.3/fpm/pool.d/www.confUse:
[php-fpm-8.3]
user = www-data
group = www-data
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660Restart the service:
sudo systemctl restart php8.3-fpmIf you also installed PHP 7.4, edit its pool:
sudo vim /etc/php/7.4/fpm/pool.d/www.confUse:
[php-fpm-7.4]
user = www-data
group = www-data
listen = /run/php/php7.4-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660Restart PHP 7.4 FPM:
sudo systemctl restart php7.4-fpmDisable commonly abused functions:
sudo vim /etc/php/8.3/fpm/php.ini
sudo vim /etc/php/7.4/fpm/php.iniSet:
disable_functions = exec,passthru,shell_exec,system,proc_open,popenEnable OPcache:
[opcache]
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.use_cwd=1
opcache.validate_timestamps=1
opcache.revalidate_freq=60
opcache.save_comments=1Install Apache
Install Apache:
sudo apt install apache2Set a global ServerName:
sudo sed -i '/# Global configuration/a ServerName localhost' /etc/apache2/apache2.conf
grep "ServerName" /etc/apache2/apache2.confEnable and start the service:
sudo systemctl enable apache2
sudo systemctl start apache2
sudo systemctl status apache2Enable the required Apache modules:
sudo a2enmod ssl
sudo a2enmod rewrite
sudo a2enmod headers
sudo a2enmod proxy_fcgi setenvifConfirm Apache is serving the default page:
http://server_ipInstall MariaDB
Install MariaDB server and client:
sudo apt install mariadb-server mariadb-clientEnable the service:
sudo systemctl enable mariadb
sudo systemctl status mariadbSecure the installation:
sudo mysql_secure_installationRecommended answers:
- press
Enterto leave the root password empty if you want local-only development first - answer
Yto switch to Unix socket authentication - answer
Yto remove anonymous users - answer
Yto disallow remote root login - answer
Yto remove the test database - answer
Yto reload privilege tables
The main MariaDB config file is:
sudo vim /etc/mysql/my.cnfInstall phpMyAdmin
Check the latest upstream release before downloading:
https://www.phpmyadmin.net/downloadsDownload the release archive used in this guide:
cd /tmp
curl -O https://files.phpmyadmin.net/phpMyAdmin/5.2.2/phpMyAdmin-5.2.2-english.tar.gzExtract it:
sudo tar -xvzf phpMyAdmin-5.2.2-english.tar.gzMove it into place:
sudo mkdir -p /usr/share/phpmyadmin
sudo mv /tmp/phpMyAdmin-5.2.2-english/* /usr/share/phpmyadmin
ls -l /usr/share/phpmyadminCreate the phpMyAdmin database and control user:
sudo mariadbRun:
CREATE DATABASE phpmyadmin;
CREATE USER 'pma'@'localhost' IDENTIFIED BY 'strong_password_here';
GRANT SELECT, INSERT, UPDATE, DELETE ON phpmyadmin.* TO 'pma'@'localhost';
FLUSH PRIVILEGES;
exitGenerate a random blowfish secret:
openssl rand -base64 24Copy the sample config and edit it:
sudo cp /usr/share/phpmyadmin/config.sample.inc.php /usr/share/phpmyadmin/config.inc.php
sudo vim /usr/share/phpmyadmin/config.inc.phpSet the generated secret:
$cfg['blowfish_secret'] = 'your_code';Add the temp directory and hide system databases:
$cfg['TempDir'] = '/tmp/phpmyadmin';
$cfg['Servers'][$i]['hide_db'] = '^information_schema|mysql|phpmyadmin|performance_schema|sys$';Set the control user credentials:
$cfg['Servers'][$i]['controluser'] = 'pma';
$cfg['Servers'][$i]['controlpass'] = 'strong_password_here';Enable storage tables:
$cfg['Servers'][$i]['pmadb'] = 'phpmyadmin';
$cfg['Servers'][$i]['bookmarktable'] = 'pma__bookmark';
$cfg['Servers'][$i]['relation'] = 'pma__relation';
$cfg['Servers'][$i]['table_info'] = 'pma__table_info';
$cfg['Servers'][$i]['table_coords'] = 'pma__table_coords';
$cfg['Servers'][$i]['pdf_pages'] = 'pma__pdf_pages';
$cfg['Servers'][$i]['column_info'] = 'pma__column_info';
$cfg['Servers'][$i]['history'] = 'pma__history';
$cfg['Servers'][$i]['table_uiprefs'] = 'pma__table_uiprefs';
$cfg['Servers'][$i]['tracking'] = 'pma__tracking';
$cfg['Servers'][$i]['userconfig'] = 'pma__userconfig';
$cfg['Servers'][$i]['recent'] = 'pma__recent';
$cfg['Servers'][$i]['favorite'] = 'pma__favorite';
$cfg['Servers'][$i]['users'] = 'pma__users';
$cfg['Servers'][$i]['usergroups'] = 'pma__usergroups';
$cfg['Servers'][$i]['navigationhiding'] = 'pma__navigationhiding';
$cfg['Servers'][$i]['savedsearches'] = 'pma__savedsearches';
$cfg['Servers'][$i]['central_columns'] = 'pma__central_columns';
$cfg['Servers'][$i]['designer_settings'] = 'pma__designer_settings';
$cfg['Servers'][$i]['export_templates'] = 'pma__export_templates';Create the phpMyAdmin tables:
sudo mysql -u root -p phpmyadmin < /usr/share/phpmyadmin/sql/create_tables.sqlCreate an Apache basic auth password file:
sudo htpasswd -c /etc/apache2/.htpasswd pmaCreate the phpMyAdmin virtual host:
sudo vim /etc/apache2/sites-available/phpmyadmin.confUse:
<VirtualHost *:443>
ServerName pma.example.com
DocumentRoot /usr/share/phpmyadmin
SSLEngine on
SSLCertificateFile /etc/ssl/certs/default.crt
SSLCertificateKeyFile /etc/ssl/private/default.key
<FilesMatch "\.php$">
SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost/"
</FilesMatch>
<Directory /usr/share/phpmyadmin/>
AddDefaultCharset UTF-8
DirectoryIndex index.php
AuthType Basic
AuthName "Restricted Access"
AuthUserFile /etc/apache2/.htpasswd
Require valid-user
Options -Indexes
</Directory>
<Directory /usr/share/phpmyadmin/setup/>
Require all denied
</Directory>
<Directory /usr/share/phpmyadmin/libraries/>
Require all denied
</Directory>
<Directory /usr/share/phpmyadmin/templates/>
Require all denied
</Directory>
<Directory /usr/share/phpmyadmin/setup/lib/>
Require all denied
</Directory>
<Directory /usr/share/phpmyadmin/sql/>
Require all denied
</Directory>
<Directory /usr/share/phpmyadmin/vendor/>
Require all denied
</Directory>
</VirtualHost>Enable the site:
sudo a2ensite phpmyadmin.confTest and reload Apache:
sudo apache2ctl configtest
sudo systemctl reload apache2Create a database and user for your application:
sudo mariadbRun:
CREATE DATABASE example_db;
CREATE USER 'example_user'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON example_db.* TO 'example_user'@'localhost';
FLUSH PRIVILEGES;
exitYou should now be able to sign in to phpMyAdmin at https://pma.example.com.
Create a Dedicated Site User
Create a non-sudo user for the hosted application:
sudo adduser --disabled-password dolphinCreate the document root and fix ownership:
sudo mkdir -p /var/www/dolphin/example.com
sudo chmod 711 /var/www/dolphin
sudo chown root:root /var/www
sudo chown -R dolphin:dolphin /var/www/dolphin
sudo chmod -R 755 /var/www/dolphin/example.comCopy the PHP 8.3 pool config:
sudo cp /etc/php/8.3/fpm/pool.d/www.conf /etc/php/8.3/fpm/pool.d/dolphin.conf
sudo vim /etc/php/8.3/fpm/pool.d/dolphin.confUse:
[php8.3-fpm-dolphin]
user = dolphin
group = dolphin
listen = /run/php/php8.3-fpm-dolphin.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660Restart PHP-FPM:
sudo systemctl restart php8.3-fpmCreate the site virtual host:
sudo vim /etc/apache2/sites-available/example.com.confUse:
<VirtualHost *:80>
ServerAdmin [email protected]
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/dolphin/example.com
<FilesMatch "\.php$">
SetHandler "proxy:unix:/run/php/php8.3-fpm-dolphin.sock|fcgi://localhost/"
</FilesMatch>
ErrorLog ${APACHE_LOG_DIR}/example.com_error.log
CustomLog ${APACHE_LOG_DIR}/example.com_access.log combined
Redirect permanent / https://example.com/
</VirtualHost>
<VirtualHost *:443>
ServerAdmin [email protected]
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/dolphin/example.com
<FilesMatch "\.php$">
SetHandler "proxy:unix:/run/php/php8.3-fpm-dolphin.sock|fcgi://localhost/"
</FilesMatch>
SSLEngine on
SSLCertificateFile /etc/ssl/certs/default.crt
SSLCertificateKeyFile /etc/ssl/private/default.key
ErrorLog ${APACHE_LOG_DIR}/example.com_ssl_error.log
CustomLog ${APACHE_LOG_DIR}/example.com_ssl_access.log combined
<Directory /var/www/dolphin/example.com>
AddDefaultCharset UTF-8
DirectoryIndex index.php
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
</VirtualHost>Enable the site and reload Apache:
sudo a2ensite example.com.conf
sudo apache2ctl configtest
sudo systemctl reload apache2Install WordPress
Switch to the site user:
sudo su - dolphinDownload and extract WordPress:
cd /var/www/dolphin/example.com
curl -O https://wordpress.org/latest.tar.gz
tar -xvzf latest.tar.gz
mv wordpress/* .
rm latest.tar.gz && rm -fr wordpressOpen https://example.com in your browser to continue the WordPress installer.