What the four packages install
A LAMP stack on Ubuntu 22.04 or 24.04 is four apt packages: apache2, mysql-server or mariadb-server, php, and the glue packages libapache2-mod-php and php-mysql. Install them in that order, because PHP's Apache module registers with a web server that already exists.
apache2is Apache 2.4 and runs asapache2.service.mysql-servergives MySQL 8.0 on both releases;mariadb-servergives MariaDB 10.6 on 22.04 and 10.11 on 24.04.phpis PHP 8.1 on 22.04 and PHP 8.3 on 24.04, both still in support.libapache2-mod-phpis mod_php, which runs PHP inside Apache with no separate FPM service.php-mysqladds the mysqli and PDO drivers PHP uses to reach the database.
Everything here comes from the Ubuntu main repository, so no PPA is involved. A tutorial that starts by adding ondrej/php is installing a version Ubuntu does not ship, which is a different job.
Install Apache and open the firewall
Apache starts on its own after install and answers on port 80 straight away. The only real decision is the firewall order: ufw must allow SSH before you enable it, or the session you are typing in dies.
- Run
sudo apt update, thensudo apt install apache2. - Confirm with
sudo systemctl status apache2: Active: active (running) and the unit enabled at boot. - Allow SSH first with
sudo ufw allow OpenSSH. - Add the web profile with
sudo ufw allow 'Apache Full', which opens 80 and 443 together, thensudo ufw enable. - Open
http://your-server-ip/and look for the Apache2 Ubuntu Default Page.
No default page means the request never reached Apache. Check sudo ss -tlnp | grep :80 for a listener, then the provider's own firewall, before you edit any configuration file.
Install MySQL or MariaDB and secure it
Pick one engine. For WordPress and most PHP applications either is fine, and the commands after the install are the same shape whichever you choose.
Run sudo apt install mysql-server or sudo apt install mariadb-server, then sudo systemctl status mysql (or mariadb). On Ubuntu the root account authenticates through the local socket, so sudo mysql opens a prompt without asking for a password.
sudo mysql_secure_installationsets a root password, drops anonymous users, blocks remote root login and removes the test database.sudo mysql -e "SELECT VERSION();"prints the server version, which is the check worth keeping.- Leave the database on
127.0.0.1; nothing in a single-server LAMP install needs port 3306 exposed.
The application database and its dedicated user are a separate five-statement job. The MySQL on Ubuntu guide gives those statements with the exact grants and the error each one throws.
Install PHP 8.x and put index.php first
One apt command installs PHP with the extensions a typical app expects. Which major version you get depends on the release, not on the command.
sudo apt install php libapache2-mod-php php-mysql php-cli php-curl php-mbstring php-xml php-zip php-gd.php -vprints the CLI version: 8.1.x on 22.04, 8.3.x on 24.04.apache2ctl -M | grep phpshould listphp_module. If it does not, runsudo a2enmod php8.3(orphp8.1) and restart Apache.- Edit
/etc/apache2/somods-enabled/ dir.conf DirectoryIndexstarts withindex.phpinstead ofindex.html. sudo apache2ctl configtestmust print Syntax OK, thensudo systemctl reload apache2.
That DirectoryIndex order explains the blank page or file listing people hit right after copying an app: Ubuntu lists index.html first, and Apache stops at the first name it finds.
Create the vhost and set ownership
The default vhost serves /var/www/html. Give a real domain its own directory and config file before you upload anything, so ownership and paths are right from the first request.
sudo mkdir -p /var/www/example..com/public_ html sudo chown -R www-data:www-data /var/www/example.com, thensudo chmod -R 755 /var/www/example.com.- Create
/etc/apache2/with asites-available/ example.com. conf <VirtualHost *:80>block holdingServerName,ServerAlias,DocumentRoot, and a<Directory>block withRequire all granted,AllowOverride AllandOptions -Indexes +FollowSymLinks. sudo a2ensite example.com.conf, thensudo a2dissite 000-default.confwhen this is the only site.sudo apache2ctl configtest, thensudo systemctl reload apache2.apache2ctl -Sshows which vhost claims the name.
Permissions are the other half of the job: directories at 755 so Apache can walk into them, files at 644, owner www-data. Setting 777 to make an upload pass is how a small permission problem becomes a security one.
Test PHP with phpinfo, then delete it
A single file proves that Apache, mod_php and the handler chain all agree. Put it in the site root once a vhost is live, not in /var/www/html.
echo '<?php phpinfo(); ?>' | sudo tee /var/www/example..com/public_ html/info. php - Open
http://example.com/info.php. You want a table headed PHP Version 8.3.x with Server API: Apache 2.0 Handler. - Note Loaded Configuration File and
memory_limitwhile the page is open; that is the php.ini your app actually reads. sudo rm /var/www/example.before you close the terminal.com/public_ html/info. php
If the browser offers to download the file instead of rendering it, mod_php is not enabled for that site: run sudo a2enmod php8.3, reload Apache and test the same URL again.
Fix 403 Forbidden and 500 Internal Server Error
Both errors sit in /var/log/apache2/error.log with a reason on the same line. Read that line first, because the browser page never carries enough detail to fix anything.
| Symptom | Cause | Fix |
|---|---|---|
| 403 on every file in the vhost | AH01630 client denied by server configuration, or a parent folder without the execute bit | Add Require all granted to the Directory block, then chmod o+x on each parent path |
| 403 on the site root only | DirectoryIndex finds index.html first, and nothing else is permitted there | Put index.php first in dir.conf, then reload Apache |
| 500 with no PHP error in the page | Invalid command 'php_value' from a .htaccess written for mod_php on a php-fpm host | Remove the php_* lines, or move that vhost to mod_php |
| 500 straight after a2ensite | A typo in the vhost, such as an unclosed </Directory> | apache2ctl configtest prints the file and the line number |
| 404 for a site that exists | The default vhost still answers for that name | a2dissite 000-default.conf, then apache2ctl -S to confirm the match |
| Access denied for user from PHP | The app connects as a user never granted that schema | Create the user at localhost and GRANT on that one database |
One habit prevents most of these: change a single thing, run sudo apache2ctl configtest, reload, and look at the site. A reload costs nothing, while a restart on a busy server drops open connections.
Where a LAMP stack should run
Root access, your own PHP version and a database you can tune are the whole point of LAMP, and shared hosting gives you none of them. A WebJi® VPS server runs Ubuntu 22.04 or 24.04 with root, so the stack above is yours to build and patch.
If you would rather not maintain Apache and MySQL by hand, cPanel hosting puts the same stack behind a panel that handles vhosts, updates and mail. VPS and managed plans are quoted by email at support@webji.services before payment, in INR before 18% GST, and the contact page reaches the team in Hindi or English.
Frequently Asked Questions
How do I install a LAMP stack on Ubuntu 22.04 or 24.04?
Run sudo apt update, then install three layers in order: sudo apt install apache2, sudo apt install mysql-server (or mariadb-server), and sudo apt install php libapache2-mod-php php-mysql. Ubuntu 22.04 gives PHP 8.1 and 24.04 gives PHP 8.3. Finish with a vhost, an a2ensite, and a phpinfo page you delete after testing.
In what order should I install Apache, MySQL and PHP?
Apache first, database second, PHP last. libapache2-mod-php registers itself with Apache when it installs, and php-mysql needs a database client library that arrives with either MySQL or MariaDB. The order does not break anything if you reverse the last two, but PHP before Apache means an extra a2enmod and reload.
How do I open the firewall for Apache on Ubuntu?
Run sudo ufw allow OpenSSH before you enable ufw, then sudo ufw allow 'Apache Full', which opens 80 and 443 in one rule, and sudo ufw enable. Confirm with sudo ufw status. If you enable ufw first on a remote server, the SSH session drops and the provider console is the only way back.
How do I test that PHP works with Apache?
Create info.php in the site root with phpinfo() in it, open it in a browser, and look for PHP Version and Server API: Apache 2.0 Handler. Delete the file straight after. If the browser downloads the file instead of rendering it, run sudo a2enmod php8.3 (or php8.1 on 22.04) and reload Apache.
MariaDB or MySQL for an Ubuntu server?
Either works for WordPress and most PHP apps. mysql-server installs MySQL 8.0 on both releases; mariadb-server installs MariaDB 10.6 on 22.04 and 10.11 on 24.04. MariaDB keeps the smaller memory footprint and the MySQL-compatible command line, while MySQL 8.0 has the newer JSON and window-function behaviour some apps now expect.
Why do I get 403 Forbidden after adding a vhost?
The Apache error log names it: AH01630 client denied by server configuration means the Directory block lacks Require all granted, or a parent folder of the DocumentRoot is missing the execute bit. Run sudo chmod o+x on each parent path, reload Apache, and check that DirectoryIndex lists index.php before index.html.
Why do I get 500 Internal Server Error on a new LAMP site?
Read /var/log/apache2/error.log; the line above the 500 says why. The two common causes are a syntax error in the vhost, which apache2ctl configtest reports with a line number, and php_value or php_flag in .htaccess on a host running php-fpm instead of mod_php, which logs Invalid command 'php_value'.

