DEV Community

Cover image for LEMP STACK ON AWS
Omolara Adeboye
Omolara Adeboye

Posted on

LEMP STACK ON AWS

Table of Content

  1. Project Overview
  2. Steps
  3. Conclusion

Project Overview

The LEMP stack is a widely used software stack for serving web applications and dynamic web pages. LEMP stands for:

  • Linux: The foundational operating system, known for its robustness and security.
  • Engine-X (Nginx): The high-performance web server that handles incoming requests and serves static content, acting as a reverse proxy and load balancer.
  • MySQL: A reliable relational database management system that stores application data in structured tables.
  • PHP: A server-side scripting language used for web development, enabling dynamic content through database interactions.

How LEMP Works

Nginx receives HTTP requests from clients. For dynamic pages, it forwards requests to PHP, which processes them, interacts with MySQL for data retrieval or storage, and sends results back to Nginx, which then responds to the client.

In this project, i will provision the AWS EC2 instance from the commandline. The required prerequisite for this is configuration of the AWS CLI.

Prerequisites

  • AWS Account
  • Ubuntu Linux
  • AWS cli

Steps

Step 0 Prepare Prerequisites

  • Install the AWS CLI. Follow this guide to install the AWS CLI on your local machine.

  • Configure AWS CLI: Run the following to configure your AWS credentials:

aws configure
Enter fullscreen mode Exit fullscreen mode

You will be required to enter your AWS Access Key ID, Secret Access Key, Region and Output format.

Run : aws configure list to verify the configuration.

aws configure list

  • Choose an AMI: You can visit the console to choose an appropriate image or run the following command:
aws ec2 describe-images --owners amazon
Enter fullscreen mode Exit fullscreen mode
  • Create a key pair (if required)

Create a key pair with the following command:

aws ec2 create-key-pair --key-name lempKey --query 'KeyMaterial' --output text > lempKey.pem
Enter fullscreen mode Exit fullscreen mode

This command shows no output. The file is created in the current working directory and can be viewed with the ls command

Create lemp key

When you check the content, it should be similar to the image below:

Cat lempkey

Set the appropriate permission:

chmod 400 lempKey.pem
Enter fullscreen mode Exit fullscreen mode
  • Security group: If not already created, create security group with SSH and http/https access.

Run the command:

aws ec2 create-security-group --group-name <MySecurityGroup> --description "Security group to allow ssh access"
Enter fullscreen mode Exit fullscreen mode

This command will show the GroupId which be used in the next step.

groupId-securitygrp

Run the following command to authorize ssh Access

aws ec2 authorize-security-group-ingress --group-id <GroupId> --protocol tcp --port 22 --cidr <Your-Ip/32>

Enter fullscreen mode Exit fullscreen mode

port 22 access

Run the following to authorize http and https access:

  • http
aws ec2 authorize-security-group-ingress --group-id <GroupId> --protocol tcp --port 80 --cidr 0.0.0.0/0

Enter fullscreen mode Exit fullscreen mode

port 80 access any

  • https
aws ec2 authorize-security-group-ingress --group-id <GroupId> --protocol tcp --port 443 --cidr 0.0.0.0/0

Enter fullscreen mode Exit fullscreen mode

port 443 access any

Verify that the rules were added correctly:

aws ec2 describe-security-groups --group-ids <GroupId>

Enter fullscreen mode Exit fullscreen mode

all security grp

  • Launch an ec2 instance: Run the command below to launch an instance
aws ec2 run-instances --image-id [ami-id] --count 1 --instance-type [instance-type] --key-name [key-pair] --security-group-ids [security-group-id]

Enter fullscreen mode Exit fullscreen mode

Replace [ami-id], [instance-type], [key-pair], [security-group-id] as appropriate

Succesfully created instance

On the console:

created instance on aws console

Note that to create the instance with a name, include a name tag as follows:

aws ec2 run-instances --image-id <ami-id> --count 1 --instance-type <instance-type> --key-name <key-pair> --security-group-ids <security-group-id> --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=lempserver}]'
Enter fullscreen mode Exit fullscreen mode
  • ssh into the server. Ensure you are in the directory where your private key is and run the following command. Otherwise, use the full path of the key.
ssh -i lempKey.pem ubuntu@54.164.242.184
Enter fullscreen mode Exit fullscreen mode

SSh with Lemp Key

Step 1 Install Nginx web server

  • Update apt packages and Install Nginx on the server
sudo apt update -y
sudo apt install nginx -y
Enter fullscreen mode Exit fullscreen mode
  • Verify the installation of Nginx and whether it is running as a service .
sudo systemctl status nginx
Enter fullscreen mode Exit fullscreen mode

Nginx running

  • Verify via curl on the terminal or on the web browser

Curl

curl http://localhost:80
Enter fullscreen mode Exit fullscreen mode

nginx on curl

web browser
Retrieve the public Ip address with the following command:

TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` && curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/public-ipv4

Enter fullscreen mode Exit fullscreen mode

View on browser:

http://[Ubuntu-webserver-IP]
Enter fullscreen mode Exit fullscreen mode

nginx on browser

Step 2 Install MySQL

  • Mysql is a popular relational database that we can use in this project. We will install it with apt.
sudo apt install mysql-server -y
Enter fullscreen mode Exit fullscreen mode

Verify with:

sudo systemctl status mysql
Enter fullscreen mode Exit fullscreen mode

Systemctl mysql

  • Login to the mysql console:
sudo mysql
Enter fullscreen mode Exit fullscreen mode
  • We will set a password for the root user with the following command:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password 
BY 'Passw0rd321@'
Enter fullscreen mode Exit fullscreen mode

It is important to use a strong password. We used Passw0rd321@ for simplicity.

Exit the mysql shell with the command exit

  • We will run a security script that comes preinstalled with Mysql to remove some insecure default settings and lock down the database system. Start the script by running:
sudo mysql_secure_installation
Enter fullscreen mode Exit fullscreen mode

secure mysql

  • Test login to mysql
sudo mysql -p
Enter fullscreen mode Exit fullscreen mode

Exit with the command exit

We will be asked to configure the VALIDATE PASSWORD PLUGIN.

Secure MySQL cont

Step 3 Install PHP

While Apache embeds the PHP interpreter in each request, Nginx requires an external program to handle PHP processing. Two packages are needed:

sudo apt install php-fpm php-mysql
Enter fullscreen mode Exit fullscreen mode

fpm stands form FastCGI process manager

Verify:

ls /var/run/php/
Enter fullscreen mode Exit fullscreen mode
sudo systemctl status php8.3-fpm #Replace with the appropriate version
Enter fullscreen mode Exit fullscreen mode

php running

Step 4 Configure Nginx to use PHP processor

  • We will set up server blocks in nginx (which is similar to virtual host in Apache) to contain the configuration details. Our domain name will be project_lemp. The default server block in nginx is found at var/www/html. We will create another directory under var/www/ to serve our domain:

First create a system user with no access to login for improved security:

sudo useradd -rs /usr/sbin/nologin project_lemp
Enter fullscreen mode Exit fullscreen mode

Then create the root directory

sudo mkdir /var/www/project_lemp
Enter fullscreen mode Exit fullscreen mode

This command produces no output if successful.

  • Assign ownership of the directory The current owner is root as seen in the image

Current owner root

sudo chown -R project_lemp:project_lemp /var/www/project_lemp
Enter fullscreen mode Exit fullscreen mode
  • Open a new configuration file in the nginx sites-available directory
sudo nano /etc/nginx/sites-available/project_lemp
Enter fullscreen mode Exit fullscreen mode

Enter the following configuration:

server {
    listen 80;
    server_name project_lemp www.project_lemp;
    root /var/www/project_lemp;  # Path to your project's root directory
    index index.html index.htm index.php;

    # Access and error logs
    access_log /var/log/nginx/project_lemp.access.log;
    error_log /var/log/nginx/project_lemp.error.log;

    # Handling static files
    location / {
        try_files $uri $uri/ =404;
    }

    # PHP-FPM Configuration
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;  # Update to the PHP version>

    }

    # Deny access to .htaccess files
    location ~ /\.ht {
        deny all;
    }
}
Enter fullscreen mode Exit fullscreen mode
  • The breakdown of the server block is as follows:
1. Server Configuration
   - listen 80;: Listens on port 80 (default HTTP port).
   - server_name project_lemp www.project_lemp;: Specifies the server name (domain or subdomain).
   - root /var/www/project_lemp;: Sets the document root directory for the project.

2. Index Files
   - index index.html index.htm index.php;: Defines the index files to serve when a directory is requested.

3. Logging
   - access_log /var/log/nginx/project_lemp.access.log;: Logs access requests.
   - error_log /var/log/nginx/project_lemp.error.log;: Logs errors.

4. Static File Handling
   - location / { try_files $uri $uri/ =404; }: Attempts to serve files from the requested URI; if not found, returns a 404 error.

5. PHP Configuration
   - location ~ \.php$ { ... }: Handles PHP files using PHP-FPM (FastCGI Process Manager).
Enter fullscreen mode Exit fullscreen mode

include snippets/fastcgi-php.conf;: Includes PHP configuration settings.
- fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;: Forwards PHP requests to PHP-FPM via a Unix socket (update the PHP version as needed).

6. Security
  - location ~ /\.ht { deny all; }: Denies access to files starting with .ht (e.g., .htaccess).
Enter fullscreen mode Exit fullscreen mode

Next activate the configuration by creating a link to the config file form nginx sites-enabled directory:

sudo ln -s /etc/nginx/sites-available/project_lemp /etc/nginx/sites-enabled/
Enter fullscreen mode Exit fullscreen mode

Test the configuration by running:

sudo nginx -t
Enter fullscreen mode Exit fullscreen mode

The configuration is ok if it shows an image similar to the one below:

nginx -t

Also, disable the default nginx host configured on port 80:

sudo unlink /etc/nginx/sites-enabled/default
Enter fullscreen mode Exit fullscreen mode

Reload nginx to apply the changes:

sudo systemctl reload nginx
Enter fullscreen mode Exit fullscreen mode

Step 5 Test server block with html

We will create a test index.html file using aws instance metadata to confirm that our server block is working as expected:

  • Create an index.html in /var/www/project_lemp with the following command and assign the correct permissions:
sudo touch /var/www/project_lemp/index.html;sudo chown -R project_lemp:project_lemp /var/www/project_lemp/index.html
Enter fullscreen mode Exit fullscreen mode

Then write the following content:

sudo bash c 'echo 'HELLO LEMP from hostname' $(TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` && curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/public-hostname) 'with public IP' $(TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` && curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/public-ipv4) > /var/www/project_lemp/index.html'

Enter fullscreen mode Exit fullscreen mode

Set the correct permissions for the file:

sudo chmod 644 /var/www/project_lemp/index.html
Enter fullscreen mode Exit fullscreen mode

metadata on index.html

Here is the image of the metadata info of our server on browser:

Hello lemp

Note: There is a difference in IP. I had to unavoidably pause the configurations. Being that I am using a cloud IDE-gitpod, the workspace and hence, the EC2 instance had to be recreated

Step 6 Test server block with php

  • Create a file named info.php in /var/www/project_lemp/.
sudo nano /var/www/project_lemp/info.php
Enter fullscreen mode Exit fullscreen mode

paste the following script:

<?php
phpinfo();
?>
Enter fullscreen mode Exit fullscreen mode

Assign the right ownership and permissions.

sudo chown project_lemp:project_lemp /var/www/project_lemp/info.php
sudo chmod 644 /var/www/project_lemp/info.php
Enter fullscreen mode Exit fullscreen mode
  • Visit the browser with the IP of the server referencing the info.php file.
http://[Public-ip]/info.php
Enter fullscreen mode Exit fullscreen mode

This shows sensitive details about the php environment and ubuntu server as shown below:

php info page

Remove the file or edit the script to contain:

<?php
echo "PHP is working";
?>
Enter fullscreen mode Exit fullscreen mode

php is working

Step 7 Retrieve data from the MySQl database with PHP

We will:

  • Create a test database with a simple 'To-do list'
  • Configure Access to the database so that nginx will query data from the db and display it.

We will create a new user with caching_sha2_password authentication method which is the default authentication introduced in MySql 8.0. Modern versions of PHP e.g PHP 7.4 and above having the associated MySQL extensions support it.

To confirm this, we will visit the php info page (Use the php info script we used previously and view it on the browser) and look for the MySQL native driver (mysqlnd) as shown:

mysqlnd

Alternatively, you can verify the availability of the necessary driver by checking the version of php you are using and the modules installed:

Check php version

php -v  
Enter fullscreen mode Exit fullscreen mode

Check php modules

php -m
Enter fullscreen mode Exit fullscreen mode

php version and modules

  • Connect to the MySQL console with root:
sudo mysql -p
Enter fullscreen mode Exit fullscreen mode
  • Create the database named example_database:
CREATE DATABASE `example_database`;
Enter fullscreen mode Exit fullscreen mode

create database

  • Create the user named example_user using caching_sha2_password as the default authentication method with the password asPassw0rd123@
CREATE USER 'example_user'@'%' IDENTIFIED WITH caching_sha2_password BY 'Passw0rd123@';
Enter fullscreen mode Exit fullscreen mode

create example user

Using the wildcard '%' means that the user can connect form any host machine and he is not retricted to logging in from the host machine lie the root user.

  • We will give the user permission over the example_database:
GRANT ALL PRIVILEGES ON example_database. * TO 'example_user'@'%';
Enter fullscreen mode Exit fullscreen mode

(Note the period in front of the database)

This command grants this user example_user full access to only the example_user database. He doesn't have access to any other databases on the server.

Exit the MySQL console:

exit
Enter fullscreen mode Exit fullscreen mode
  • Test that the new user is properly authenticated by logging in again:
mysql -u example_user -p
Enter fullscreen mode Exit fullscreen mode

Note:
In older legacy applications using old versions of php you cn use the mysql_native_password method of authentication.

View the database and create the todo_list table:

  • View database with the following command:
SHOW DATABASES;
Enter fullscreen mode Exit fullscreen mode

show databases

  • Select the database:
USE example_database;
Enter fullscreen mode Exit fullscreen mode

Use database

  • Create a table with the following command:
CREATE TABLE todo_list (
    item_id INT AUTO_INCREMENT PRIMARY KEY,
    task VARCHAR (255) NOT NULL,
    status ENUM('pending', 'completed') DEFAULT 'pending'    
);
Enter fullscreen mode Exit fullscreen mode
  • Insert a few rows of content to the table
INSERT INTO todo_list (task, status) VALUES
('Buy groceries', 'pending'),
('Walk the dog', 'pending'),
('Finish the report', 'pending'),
('Read a book', 'pending'),
('Prepare dinner', 'pending'); 
Enter fullscreen mode Exit fullscreen mode
  • Confirm the data you entered by running:
SELECT * FROM todo_list;
Enter fullscreen mode Exit fullscreen mode

Select from table

Exit the MySQL console.

  • Create a PHP script that will connect to MySQL and query the content:

First create th PHP file:

vi /var/www/project_lemp/todo_list.php
Enter fullscreen mode Exit fullscreen mode

Enter the following script that connects to the MySQL database and queries the content of the todo_list. It also displays the results in a list. If there is a problem with the database connection , it will throw an exception.

<?php
// Database configuration
$host = 'localhost'; // Change if necessary
$database = 'example_database';
$user = 'example_user';
$password = 'Passw0rd123@'; // Replace with your actual password
$table = "todo_list";

try {
    // Establish database connection
    $db = new PDO("mysql:host=$host;dbname=$database", $user, $password);

    // Set PDO to throw exceptions on error
    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    // Prepare and execute the query
    $query = "SELECT item_id, task, status FROM $table";
    $stmt = $db->query($query);
    $todos = $stmt->fetchAll(PDO::FETCH_ASSOC);

    // Display results
    if ($todos) {
        echo "<h1>To-Do List</h1>";
        echo "<ul>";
        foreach ($todos as $todo) {
            echo "<li>ID: {$todo['item_id']}, Task: {$todo['task']}, Status: {$todo['status']}</li>";
        }
        echo "</ul>";
    } else {
        echo "No tasks found in the to-do list.";
    }
} catch (PDOException $e) {
    // Handle connection errors
    echo "Database connection failed: " . $e->getMessage();
}
?>
Enter fullscreen mode Exit fullscreen mode
  • The functionality of the script

This PHP script connects to a MySQL database, retrieves data from a "todo_list" table, and displays the tasks in an unordered list.

  • Functionality Breakdown:
1. Database Configuration: The script defines database connection parameters (host, database name, username, password, and table name).

2. Database Connection: It establishes a connection to the database using PDO (PHP Data Objects) and sets the error mode to throw exceptions.

3. Query Execution: The script executes a SELECT query to retrieve all tasks from the "todo_list" table.

4. Data Retrieval: It fetches all query results as an associative array.

5. Displaying Results: If tasks exist, it displays them in an unordered list with task ID, description, and status. Otherwise, it shows a "No tasks found" message.

6. Error Handling: The script catches and displays any PDO exceptions that occur during database connection or query execution.
Enter fullscreen mode Exit fullscreen mode
  • Visit the browser and you will see an image similar to the one below:
http://[Publi-ip]/todo_list.php
Enter fullscreen mode Exit fullscreen mode

todo_list on browser

Conclusion

In this project, we successfully deployed a LEMP stack on AWS, providing a robust and efficient environment for serving web applications. By implementing a test database and creating a dynamic PHP script, we demonstrated how to interact with the database, showcasing the full capabilities of the LEMP stack. This hands-on experience solidified our understanding of server configurations and database management in a cloud environment.

Top comments (0)