Home » Linux » Raspberry Pi » Lamp Stack Ubuntu Raspberry Pi

Setting up a LAMP Stack on Ubuntu 20.04 (And Raspberry Pi)

This tutorial covers setting up a full LAMP (Linux, Apache, MySQL, PHP) web stack, including HTTP server, MySQL database, and PHP for app logic.

This is commonly called a LAMP server – Linux, Apache, MySQL, PHP.

Linux rules the web server world – the operating system powers the vast majority of web servers worldwide.

Apache is a popular web server – the software with which web browsers connect to receive content.

MySQL is a popular database server – it stores information in rows and columns inside tables. There is also a drop-in replacement called MariaDB, which offers compatible functionality.

Finally, PHP is a programming language commonly used in web development. It lets you write application logic, which then generates web pages.

Apache runs on Linux, serving content generated by PHP, which stores user data in MySQL databases.

Here’s how to set it all up and get it working together.

Installing Ubuntu Linux

First up, the Linux bit. You’ll need to install Ubuntu Linux for this tutorial. 20.04 is the latest release with Long Term Support recommended for servers. Ubuntu was chosen as it is popular, stable, and its software repositories already contain everything needed.

You can download the Server or Desktop variations – either will work for setting up a LAMP server, which you choose depends on whether you plan on using it for local development on a desktop computer with a GUI and development tools or whether you’re deploying a server which will live in the cupboard, or on a cloud host.

Here’s how to Install Ubuntu Desktop

And Here’s How To Install Ubuntu MATE on a Raspberry Pi

If you’re deploying Ubuntu Server to a cloud host, the OS install is taken care of for you.

Once you’ve got Ubuntu installed, make sure it’s up-to-date by running the following commands in succession:

sudo apt update
sudo apt upgrade

You may be more familiar with using the apt-get command – see this article on the difference to the apt command.

Setting a Static IP

If you plan on running your LAMP server on your local network, it might be useful to set a static IP address so you always know where to find it – click here to check out how.

Setting up Remote Access with SSH

Whether you’re developing on a local machine with a desktop or on a headless server in the cloud, remote access will come in useful. Most cloud-hosted servers will already be set up with SSH and a user account. If not, here’s how to install it:

sudo apt install openssh-server

See this article for more on connecting to your Raspberry Pi or Linux computer via SSH, including finding the IP address.

Setting up the Firewall with UFW

UFW (Uncomplicated Firewall) is a simple firewall that will help keep your server safe. Leaving a server wide-open to internet traffic is a certified bad idea. Even if you are running a server on your home network, it’s a good idea to have a firewall set up so you’re familiar with the process.

Install UFW:

sudo apt install ufw

Allow SSH through the firewall:

sudo ufw allow OpenSSH

This will allow both incoming and outgoing traffic to SSH from the server while the firewall is enabled. Enable the firewall by running:

sudo ufw enable

sudo ufw enable

Don’t enable the firewall until you’ve allowed SSH through! Otherwise, you might block your own remote session and be forever locked out!

Preventing Brute-Force Attacks with Fail2ban

Fail2ban blocks all internet traffic from IP addresses, which repeatedly fail to log in to services running on your server (Like SSH, Apache, and Mysql). It’s a good start to securing your internet-facing Linux computers but is not a stop-all – it just helps prevent automated attacks.

Once installed, it’ll do its thing without any further configuration:

sudo apt install fail2ban

Installing the Apache Web Server

To install the Apache Web Server, run:

sudo apt install apache2

Apache will be installed and set to automatically start on boot.

Allow Apache Through the Firewall

Apache is a web server, so it will also need to be allowed through the firewall. You can list the available firewall profiles by running:

sudo ufw app list

sudo ufw app listIn the screenshot, you can see the programs that are registered for the firewall – including SSH, which we allowed previously. Allow incoming traffic only for Apache by running:

sudo ufw allow in "Apache Full"

Check the status to make sure Apache is allowed by running:

sudo ufw status

sudo ufw status

Checking Apache is Running

If you’re setting up LAMP on a desktop computer with a web browser, navigate to

In your browser – you should see the below page confirming Apache is up and running:

Checking Apache is Running is the address for localhost – it’s the address that a computer uses to access itself on a network.

If you are on a different computer, you can connect using your LAMP server’s IP address – there’s a link further up in this article about setting a static IP address.

Apache’s Web Files

By default, apache stores the HTML (and soon PHP) files that run your website at:


Installing the MySQL Database Server

Next up, install the MySQL server:

sudo apt install mysql-server

Like Apache, it will be set to start automatically on boot.

Secure MySQL

MySQL should be secured before you continue. There’s a built-in tool that will do all of this for you, which can be run by entering:

sudo mysql_secure_installation

You’ll be asked some questions about how you want MySQL secured:

Would you like to setup VALIDATE PASSWORD component?

Yes – enforcing some password complexity will make the system more secure.

You’ll then be prompted to set password complexity and enter a new password for the MySQL root (administrator) user. The MySQL root user is completely separate from the Linux root user.

Remove anonymous users?

Also, answer yes – the default anonymous users do not have passwords, which is insecure.

Disallow root login remotely?

Again, answer yes. There is no need for remote users to perform administrative tasks on MySQL.

We will also not be allowing MySQL through the firewall. It will be accessible from within the server but not externally. There’s no need for it to be.

Remove test database and access to it?


Reload privilege table now?

Answer yes to complete securing the server and reload the remaining users’ privileges.

Done!sudo mysql_secure_installation

Check MySQL is Running

To confirm that MySQL is running run:

sudo mysql

When the MySQL command is run as root (or using sudo), it automatically tries to connect to the local server as the root user.

If this succeeds, run the following to list the databases present to confirm everything works correctly:





To exit the MySQL command line.

Installing PHP

Install PHP with the following familiar-looking command:

sudo apt install php

Check PHP is Installed

To check PHP has installed successfully, run:

php -v

php -v

Install PHP Modules

PHP needs to be able to talk with Apache and Mysql. For this to happen, Apache needs its PHP module installed, and PHP needs its MySQL module installed:

sudo apt install libapache2-mod-php php-mysql

These Apache and PHP modules will be enabled automatically once installed.

Checking phpinfo()

phpinfo() is a PHP function that outputs all of your PHP environment details, including configuration and which modules are enabled. It’s a good way to check that Apache and PHP are configured.

Create a new PHP file to run this in the Apache HTML directory, which holds the files your web server serves:

sudo nano /var/www/html/info.php

This will open the nano text editor with an empty file called info.php. Enter the code:



And press CTRL + X to save and quit and Y to confirm.

If you navigate to /info.php on your new LAMP server, you should see the following:info.php

Testing it All Together

Everything that makes a LAMP server a LAMP server is now installed. Congratulations!

To confirm that everything is working as it should be, ready to start hosting existing apps or developing new ones, here are some steps for building a test PHP script that reads from a MySQL database.

Creating a Database and User

Open the MySQL command line as the root user:

sudo mysql

Create a database called db_example:


Next, a user is created with the username usr_example, which will have access from localhost using the supplied password:

CREATE USER 'usr_example'@'localhost' IDENTIFIED WITH mysql_native_password BY 'MyPassword1!';

With the database and user-created, grant the user ALL privileges for that database:

GRANT ALL ON db_example.* TO 'usr_example'@'localhost';

You can confirm that the database was created by running:



Create a Table and Records

The database and user with access rights to the database are ready to go – now we need to create a table with some data in it to test with.

Switch to the db_example database by running:

USE db_example;

You must switch to the database before creating tables – otherwise, MySQL won’t know which database to put the table in.

Create a table called fruit – it will be created in db_example:


The line above tells MySQL to create a fruit table with two columns – id and name.

id is an INTeger column that will auto-increment – each record will have a unique id number, which is an increment of 1 over the previous.

name is a VARCHAR column, which is a short string with a maximum length given as 255 characters.

The final part of the statement tells MySQL that the primary key will be the id column. The primary key is the column used to uniquely identify each record.

You’ve probably noticed that I capitalize all MySQL commands in the above statements – this is optional, but it is a convention as it makes it easy to see what words are MySQL statements and what words refer to table names, etc.

Now, insert some test data. A list of fruit! Only the name needs to be supplied – the id column is calculated and entered automatically:

INSERT INTO fruit(name) VALUES ('apples'), ('bananas'), ('pears'), ('grapes');

Whenever MySQL does something right, you’ll get a response with:

Query OK

And whenever something goes wrong, you’ll get an error.

Check the records were entered correctly into the fruit table by running:

SELECT * from fruit;

SELECT * from fruit;And exit MySQL:


PHP Script to Read Database, Table, and Records

Use the nano text editor to create a new PHP file to list the contents of the fruit table we just created in the db_example database:

sudo nano /var/www/html/db_test.php

And enter the following code:


ini_set('display_startup_errors', 1);
ini_set('display_errors', 1);

$db_user = "usr_example";
$db_password = "MyPassword1!";
$db_host = "localhost";
$db_database = "db_example";
$db_table =  "fruit";

$connection = new mysqli($db_host, $db_user, $db_password, $db_database);

if ($connection->connect_errno) {
    printf("Connection failed: %s\n", $connection->connect_error);

$test_query = "SELECT * FROM $db_database.$db_table;";
$result = $connection->query($test_query);

while( $row = $result->fetch_array() )
    echo $row["name"];
    echo "<br />";



Save it by pressing CTRL + X and Y to confirm, then point your web browser to /db_test.php on your new LAMP server:

lamp php test 1

Looks good! All of the components of your LAMP server are working together!

Real-World Usage for your LAMP Server.

Linux, Apache, MySQL, and PHP are all versatile tools. You can build pretty much whatever you want on this platform. There are hordes of helpers, libraries, tutorials, articles, and helpful people on the Internet ready to help you start building your next project.

PHP Frameworks are useful for providing the foundation of a project if you want a leg up rather than starting from scratch.

WordPress provides a blogging platform that can be extended with thousands of themes and packages that add functionality from online stores to social network functionality. It works out of the box and can be extended with your own code if you’re feeling adventurous.

Laravel provides a bare framework for building literally anything – APIs, chat apps, online calendars, doorbells, whatever you can think of.

Click here to check out our other PHP articles.

If you’re looking for real-world code examples, subscribe to LinuxScrew on Twitter – I’ll be posting various projects, some of which will include PHP code.

Photo of author
I'm Brad, and I'm nearing 20 years of experience with Linux. I've worked in just about every IT role there is before taking the leap into software development. Currently, I'm building desktop and web-based solutions with NodeJS and PHP hosted on Linux infrastructure. Visit my blog or find me on Twitter to see what I'm up to.

3 thoughts on “Setting up a LAMP Stack on Ubuntu 20.04 (And Raspberry Pi)”

  1. Hello, I came across a pretty challenging error (for a noob like myself) when following the turorial ‘Setting up a LMAP Stack on Ubuntu 20.04 (And Raspberry Pi)’ that I felt might be worth pointing out.

    All info pertains to section 5 ‘Testing it All Together’. This is my first write-up, so please bear with me. I will try to detail what I encountered, as well as my simple solution.


    Following the tutorial as written results in an error: ‘No database selected’ – the next logical step is to select that database and continue, which results in the provided script throwing a fatal error.


    MySQL Ver 8.0.26-0ubuntu0.20.04.3 for Linux on x86_64 ((Ubuntu))

    PHP 7.4.3 (cli)
    Zend Engine v3.4.0

    Apache/2.4.41 (Ubuntu)


    * Section 5.1 Creating a Database and User *

    [Step 1] CREATE DATABASE db_example;

    [Step 2] CREATE USER ‘usr_example’@’localhost’ IDENTIFIED WITH mysql_native_password BY ‘MyPassword1!’;

    [Step 3] GRANT ALL ON db_example TO ‘usr_example’@’localhost’;

    *** This is where I encounter my issue. ***

    Running this command results in: ‘ERROR 1046 (3D000): No database selected

    I ran the command ‘USE db_example;’ then re-ran the command from [Step 3].

    I followed the remaining steps in Sections 5.2 and 5.3 which resulted in an error displayed when visiting ‘localhost/db_test.php’ with a browser:
    Fatal error: Uncaught Error: Call to a member function fetch_array() on bool in /var/www/html/db_test.php:17 Stack trace: #0 {main} thrown in /var/www/html/db_test.php on line 17

    After some blind troubleshooting I discovered the issue – Because I had selected the database with ‘USE db_example;’ and then re-ran the command from [Step 3] I had granted ‘usr_example’ ALL permissions to ‘db_example.db_example’

    I revoked the incorrect permission, and ran ‘GRANT ALL ON db_example.* to ‘usr_example’@’localhost’; ‘

    This granted me ALL privileges on ‘db_example.*’ and the script was returning the correct data.

    Hopefully this can help someone if they encounter the same issue. Happy tinkering!

    – BuddhaHead

    • Nice diagnosis and fix! There was a typo in my MySQL query which may have contributed to this, which is now fixed 🙂

      Thanks for the update!


Leave a Comment