Install and Configure the LAMP Stack on Ubuntu 20.04

The LAMP stack—Linux, Apache, MySQL, and PHP—is a suite of open-source tools for building and hosting dynamic web applications. In this setup:

  • Linux serves as the operating system
  • Apache delivers web content
  • MySQL manages databases
  • PHP processes dynamic content

This guide shows how to install and configure the LAMP stack on Ubuntu 20.04 to host web applications on your server.

Prerequisites

Before starting, make sure you have the following:

  • An Ubuntu 20.04 server
  • SSH access as a non-root user with sudo privileges
  • A new A record for your domain pointing to the server’s IP address
  • An updated server

Install Apache

Ubuntu 20.04’s default APT repositories provide the latest Apache version. Follow these steps to update the package index and install Apache.

Update the package index

Install Apache

$ sudo apt install apache2 -y

Start Apache

$ sudo systemctl start apache2

Enable Apache to start on boot

$ sudo systemctl enable apache2

Check Apache service status

$ sudo systemctl status apache2

Sample output:

● apache2.service - The Apache HTTP Server
     Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2025-04-06 10:56:28 UTC; 20s ago
       Docs: https://httpd.apache.org/docs/2.4/
   Main PID: 2622 (apache2)
      Tasks: 55 (limit: 9415)
     Memory: 6.9M
     CGroup: /system.slice/apache2.service
             ├─2622 /usr/sbin/apache2 -k start
             ├─2623 /usr/sbin/apache2 -k start
             └─2624 /usr/sbin/apache2 -k start

Allow HTTP traffic through the firewall

Open a browser and visit your domain or server IP (for example http://SERVER-IP) to confirm that Apache’s default page appears.

Install MySQL

MySQL provides the database backend for the LAMP stack, though you can use MariaDB as an alternative. Ubuntu 20.04’s default repositories contain the latest MySQL package. Use these commands to install MySQL via APT.

Install the MySQL server package

$ sudo apt install -y mysql-server

Enable MySQL to start on boot

$ sudo systemctl enable mysql

Start MySQL

$ sudo systemctl start mysql

Check MySQL service status

$ sudo systemctl status mysql

Sample output:

● mysql.service - MySQL Community Server
     Loaded: loaded (/lib/systemd/system/mysql.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2025-04-06 10:58:51 UTC; 41s ago
   Main PID: 17189 (mysqld)
     Status: "Server is operational"
     Tasks: 38 (limit: 9415)
     Memory: 364.8M
     CGroup: /system.slice/mysql.service
             └─17189 /usr/sbin/mysqld

If the status shows active (running), the MySQL server is operational.

Secure the MySQL installation

Run the MySQL secure installation script to remove unsafe defaults and enable authentication.

$ sudo mysql_secure_installation

When prompted, configure the options as follows:

  • Enable password validation: y
  • Choose password policy: 2 for strong passwords
  • Remove anonymous users: y
  • Disallow remote root login: y
  • Remove test database: y
  • Reload privilege tables: y

You should see a confirmation similar to “Success. All done!” when the configuration is complete.

Log in to the MySQL console as root

Set a strong password for the root user

Replace Strong@@password123 with your own secure password.

mysql> ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'Strong@@password123';

Apply privilege changes

Exit the MySQL console

Log in again with the new password

Create a sample database

mysql> CREATE database content_database;

List databases to confirm

Expected output:

+--------------------+
| Database           |
+--------------------+
| information_schema |
| content_database   |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.01 sec)

Create a new MySQL user

Create a user such as dbadmin with a strong password (replace the example password with your own).

mysql> CREATE USER 'dbadmin'@'localhost' IDENTIFIED BY 'Strong@@password123';

Grant privileges on the database

mysql> GRANT ALL PRIVILEGES ON content_database.* TO 'dbadmin'@'localhost';

Apply privilege changes

Exit the MySQL shell

Install PHP and Configure PHP-FPM

PHP is a crucial part of the LAMP stack, responsible for processing dynamic content and communicating with the MySQL database. PHP-FPM (FastCGI Process Manager) improves performance by handling PHP requests through a pool of worker processes.

Install PHP and PHP-FPM

$ sudo apt install -y php php-fpm

Install common PHP extensions

$ sudo apt install -y php-mysql php-opcache php-cli libapache2-mod-php

This command installs:

  • php-mysql: Enables PHP to connect to MySQL
  • libapache2-mod-php: Allows Apache to execute PHP scripts
  • php-opcache: Caches precompiled PHP scripts for faster performance
  • php-cli: Provides command line interface access to PHP

Check the PHP version

Sample output:

PHP 7.4.3-4ubuntu2.29 (cli) (built: Mar 25 2025 18:57:03) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.3-4ubuntu2.29, Copyright (c), by Zend Technologies

Start and enable PHP-FPM

Start the PHP-FPM service according to your installed version (for example, PHP 7.4):


$ sudo systemctl start php7.4-fpm


$ sudo systemctl enable php7.4-fpm

Check PHP-FPM status

$ sudo systemctl status php7.4-fpm

Sample output:

● php7.4-fpm.service - The PHP 7.4 FastCGI Process Manager
     Loaded: loaded (/lib/systemd/system/php7.4-fpm.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2025-04-06 11:11:45 UTC; 10min ago
       Docs: man:php-fpm7.4(8)
    Process: 27868 ExecStartPost=/usr/lib/php/php-fpm-socket-helper install /run/php/php-fpm.sock /etc/php/7.4/fpm/pool.d/www.conf 74 (code=exited, status=0/SUCCESS)
   Main PID: 27851 (php-fpm7.4)
     Status: "Processes active: 0, idle: 2, Requests: 0, slow: 0, Traffic: 0req/sec"
      Tasks: 3 (limit: 9415)
     Memory: 7.3M
     CGroup: /system.slice/php7.4-fpm.service
             ├─27851 php-fpm: master process (/etc/php/7.4/fpm/php-fpm.conf)
             ├─27866 php-fpm: pool www
             └─27867 php-fpm: pool www

Configure PHP-FPM

PHP-FPM improves PHP application performance by managing worker process pools. Adjust the default pool settings based on your server’s memory. Follow these steps to integrate PHP-FPM with Apache and fine-tune the pool configuration.

Enable Apache modules required for PHP-FPM

$ sudo a2enmod proxy_fcgi setenvif

This command enables:

  • proxy_fcgi: Allows Apache to act as a proxy for PHP-FPM
  • setenvif: Sets environment variables to connect Apache and PHP-FPM

Enable default PHP-FPM configuration

$ sudo a2enconf php7.4-fpm

Restart Apache to apply changes

$ sudo systemctl restart apache2

Edit PHP-FPM pool configuration


$ cd /etc/php/7.4/fpm/pool.d/


$ sudo nano /etc/php/7.4/fpm/pool.d/www.conf

Verify the default pool name:

Ensure the following directives are set to www-data:

user = www-data
group = www-data
listen.owner = www-data
listen.group = www-data

Adjust these settings as needed:

  • pm: Set to dynamic to let PHP child processes scale with demand.
  • pm.start_servers: Number of child processes to start (default: 2).
  • pm.max_children: Maximum concurrent child processes (default: 5).
  • pm.min_spare_servers: Minimum idle child processes (default: 1).
  • pm.max_spare_servers: Maximum idle child processes (default: 3).
  • pm.max_requests: Number of requests a child process serves before recycling.

Save and close the file.

Restart PHP-FPM to apply changes

$ sudo systemctl restart php7.4-fpm

Configure Apache with PHP-FPM

Apache interacts with PHP-FPM through the mod_proxy_fcgi module, using either a UNIX socket or the default TCP port 9000. Follow these steps to create a new Apache virtual host that connects to PHP-FPM through the UNIX socket.

Remove the default Apache virtual host configuration

$ sudo rm -rf /etc/apache2/sites-enabled/000-default.conf && sudo rm -rf /etc/apache2/sites-available/000-default.conf

Create a new Apache virtual host configuration file

For example, create app.example.com.conf:

$ sudo nano /etc/apache2/sites-available/app.example.com.conf

Add the following content (replace app.example.com with your actual domain):

ServerAdmin webmaster@app.example.com
ServerName app.example.com
DocumentRoot /var/www/html/app.example.com


    Options Indexes FollowSymLinks
    AllowOverride All
    Require all granted



   SetHandler "proxy:unix:/var/run/php/php7.4-fpm.sock|fcgi://localhost/"


ErrorLog ${APACHE_LOG_DIR}/app.example.com_error.log
CustomLog ${APACHE_LOG_DIR}/app.example.com_access.log combined

This configuration:

  • Listens on port 80 for the domain app.example.com.
  • Sets the web root to /var/www/html/app.example.com.
  • Forwards PHP file requests to the PHP-FPM socket via FastCGI.
  • Defines custom paths for error and access logs.

Enable the new Apache virtual host

$ sudo a2ensite app.example.com.conf

Test the Apache configuration

$ sudo apache2ctl configtest

Sample output:

Create the virtual host web root directory

$ sudo mkdir -p /var/www/html/app.example.com

Create a sample PHP file

$ sudo nano /var/www/html/app.example.com/info.php

Add the following PHP code:

This script displays PHP version details and enabled modules in your browser.

Restart Apache to apply changes

$ sudo systemctl restart apache2

Finally, open your domain in a browser (for example http://app.example.com/info.php) to confirm that the PHP information page is displayed.

Secure the Server

Ubuntu 20.04 servers can have the Uncomplicated Firewall (UFW) enabled. Apache serves dynamic web content over HTTP port 80, while MySQL (3306) and PHP-FPM (9000) use internal TCP ports. Follow these steps to allow traffic on port 80 and configure trusted SSL certificates for HTTPS on port 443.

Configure the Firewall

Check that the firewall is active:

Sample output:

List available UFW application profiles:

Sample output:

Apache
Apache Full
Apache Secure
OpenSSH

Allow the Apache Full profile to enable both HTTP and HTTPS:

$ sudo ufw allow "Apache Full"

Reload the firewall to apply the changes:

Check UFW status again to confirm that Apache connection rules are active:

Sample output:

To                         Action      From
--                         ------      ----
1022/tcp                   ALLOW       Anywhere
Apache Full                ALLOW       Anywhere
1022/tcp (v6)              ALLOW       Anywhere (v6)
Apache Full (v6)           ALLOW       Anywhere (v6)

Generate Trusted Let’s Encrypt SSL Certificates

Install the Certbot client using Snap:

$ sudo snap install certbot --classic

Request a new SSL certificate (replace app.example.com and admin@example.com with your own domain and email):

$ sudo certbot --apache -d app.example.com -m admin@example.com --agree-tos

Sample output:

Requesting a certificate for app.example.com

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/app.example.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/app.example.com/privkey.pem
This certificate expires on 2025-07-05.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.

Deploying certificate
Successfully deployed certificate for app.example.com to /etc/apache2/sites-available/000-default-le-ssl.conf
Congratulations! You have successfully enabled HTTPS on https://app.example.com

Test the automatic SSL renewal process:

$ sudo certbot renew --dry-run

Restart Apache to apply the SSL configuration:

$ sudo systemctl restart apache2

Test the LAMP Stack Installation

Create a sample table in the existing content_database database to display “Hello World! Greetings from centron” via a PHP application.

Log in to MySQL as dbadmin

Enter the password for the dbadmin user when prompted.

Switch to the sample database

mysql> USE content_database;

Create the messages table

mysql> CREATE TABLE IF NOT EXISTS messages (
       content_id INT AUTO_INCREMENT PRIMARY KEY,
       content VARCHAR(255) NOT NULL
      );

This table includes:

  • content_id: Auto-incrementing primary key
  • content: Text column for up to 255 characters

Insert a sample row

mysql> INSERT INTO messages (content) VALUES ('Hello World! Greetings from centron');

View the table data

mysql> SELECT * from messages;

Sample output:

+----+------------------------------------------+
| content_id | content                           |
+------------+-----------------------------------+
|  1         | Hello World! Greetings from centron|
+------------+-----------------------------------+
1 row in set (0.00 sec)

Exit the MySQL console

Create a sample PHP application

Create the setup.php file in your web root directory:

$ sudo nano /var/www/html/app.example.com/setup.php

Add the following code:

<?php
$hostname = “localhost”;
$username = “dbadmin”;
$password = “Strong@@password123”;
$dbname = “content_database”;

// Establish Connection
$conn = new mysqli($hostname, $username, $password, $dbname);

// Check connection
if ($conn->connect_error) {
die(“Connection Failed: ” . $conn->connect_error);
}

$sql = “SELECT content FROM messages”;
$result = $conn->query($sql);

if ($result && $result->num_rows > 0) {
$row = $result->fetch_assoc();
echo “<h2 style=’color: blue; text-align: center; margin-bottom: 15px;’>”
. htmlspecialchars($row[“content”]) . “</h2>”;
} else {
echo “<h1>No records found.</h1>”;
}

$conn->close();
?>


This script connects to the content_database and retrieves data from the messages table. If no records are found, it displays “No records found.” If the database connection fails, it shows a connection error.

Set directory permissions

$ sudo chown -R www-data:www-data /var/www/html/app.example.com/

Open your domain in a browser (for example https://app.example.com/setup.php) to confirm that the PHP application displays “Hello World! Greetings from centron”.

Conclusion

You have successfully installed and configured Apache, MySQL, and PHP (LAMP stack) on Ubuntu 20.04. You also created sample dynamic applications to test the integration of all components and ensured the server runs securely.

Source: vultr.com

Create a Free Account

Register now and get access to our Cloud Services.

Posts you might be interested in: