Rizwan Rizwan
HOME SERVICES CASE STUDIES BLOG ABOUT $100 USD FREE Consultation Call →
DEVOPS

Ultimate LEMP Stack Installation Guide for Ubuntu

In this article, you'll install PHP 8.3, NGINX, MySQL, PHPMyAdmin, SSL, Composer and NPM on a fresh Ubuntu installation.

Rizwan Rizwan July 19, 2025 5 min read
Ultimate LEMP Stack Installation Guide for Ubuntu

Table of Contents

If you’re an old school developer like me, you probably prefer setting up server yourself. LEMP Stack is my go-to stack for a Web Server. And we all love the good old PHPMyAdmin for database administration.

What is LEMP

A LEMP stack is a popular open-source web server setup used to host dynamic websites and apps. The acronym stands for Linux, Nginx (pronounced “Engine-X”), MySQL (or MariaDB), and PHP. Together, they form a fast, reliable environment for serving modern PHP applications. When people refer to a LEMP server, they usually mean a VPS or cloud instance running all four components, configured to serve web traffic efficiently.

In this comprehensive guide, you’ll install:

  1. PHP 8.3 (with PHP-FPM)
  2. NGINX
  3. MySQL Server
  4. PHPMyAdmin
  5. Composer
  6. Node + NPM
  7. And finally a free SSL from Let’s Encrypt via CertBot

If you’re looking for commands or codes ONLY and don’t have time to read this article, you can find it on this secret gist I made. You're welcome :)

Prerequisites

The first thing is to make sure your package repos are up to date, for that run this:

apt update

This will update your VPS’ packages list.

Next, upgrade your packages that are pre-installed:

apt upgrade

If you’re running Ubuntu 24.04 LTS or later, you can skip this step. For Ubuntu 22.04 LTS or older, you also need to add Odrej’s PHP repository with this:

apt-get install ca-certificates apt-transport-https software-properties-common
add-apt-repository ppa:ondrej/php
apt update

The second command would ask you to hit “Enter” twice for confirmation.

1. Installing PHP 8.3

Run this command to install PHP 8.3-fpm module:

apt install php8.3-fpm

Also install some essential packages we’ll need:

apt install php-mbstring php-zip php-gd php-json php-curl php-intl unzip curl npm

2. Installing NGINX

Once you install php 8.3-FPM, install NGINX:

apt install nginx

3. Enable Firewall

On most VPS, and on all the droplets of DigitalOcean, UFW (or Uncomplicated Firewall) is pre-installed. You just need to configure and enable it. Since our server will only be running a web server (port 80 and 433) and we’ll need SSH access to it as well (port 22), you need to allow these ports by running the following commands:

ufw allow 80
ufw allow 443
ufw allow 22

These will allow traffic on port 80, 443 and 22. Optional, but if you have a static IP you can restrict SSH so only your workstation/VPN can connect (replace 131.5.13.44 with your actual IP):

sudo ufw allow from 131.5.13.44 to any port 22 proto tcp

Next, run this command to enable the firewall:

ufw enable

4. Install MySQL and PHPMyAdmin

Run this command to install MySQL server:

apt install mysql-server

Once the MySQL server is installed, it’ll ask you to run mysql_secure_installation but you shouldn’t right away. Securing MySQL Installation can lead to password validation issues when installing PHPMyAdmin, so once the MySQL Server is installed you should install PHPMyAdmin:

apt install phpmyadmin

During installation it’ll ask you to select your web server and the list would contain “Apache2” and “lighttpd”. Since we’re using NGINX, you can skip this step by just hitting “enter”.

Next, it’ll ask you to “Configure database for phpmyadmin with dbconfig-common?”. Here you need to select “Yes”. And on the next screen, where it’ll ask you to enter a password, just hit “enter” with empty password, the installation will generate a password on it’s own.

Now you can run this command to secure installation:

mysql_secure_installation

The mysql_secure_installation command will ask you a series of questions:

Prompt Reply With Why
Enable VALIDATE PASSWORD component? Y, level 2 (strong) Enforces long, mixed-case passwords
Remove anonymous users? Y Closes guest accounts
Disallow root login remotely? Y Forces you to SSH into the box first
Remove test database? Y Deletes the default world-readable DB
Reload privilege tables now? Y Applies changes instantly

These choices shut down the two easiest attack vectors: remote‐root logins and weak passwords.

Need to automatically restart when MySQL crashes? Check out my MySQL auto-restart tutorial

Now link the phpmyadmin directory to root of the web server (we’ll configure NGINX in the next step):

ln -s /usr/share/phpmyadmin /var/www/html/phpmyadmin

5. NGINX Configuration

Delete the default NGINX site:

rm /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default

Create a new NGINX Site config with nano:

nano /etc/nginx/sites-available/yourwebsite.com

You can paste the following config and edit to your requirement:

server {
    listen 80;
    server_name yourwebsite.com www.yourwebsite.com;

    root /var/www/html;
    index index.php index.html index.htm;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
    }

    location ~ /\. {
        deny all;
    }

    # auth_basic "Restricted Content";
    # auth_basic_user_file /etc/nginx/.htpasswd;

}

If this is a staging/test server, you might want to uncomment the auth_basic code from line 21 and 22. And make sure the auth user file exists, by running this command:

sh -c 'printf "username:%s\n" "$(openssl passwd -6)" > /etc/nginx/.htpasswd'

This will ask you to enter password, then confirm password and then save the .htpasswd file.

Once you save the site config in /etc/nginx/sites-available/yourwebsite.com you’ll now link it to Enabled Sites:

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/

Run this command to check for any errors in NGINX config:

sudo nginx -t

If it’s all good, run this to restart NGINX server:

sudo systemctl restart nginx

6. Install Free SSL via CertBot

Install packages for Certbot and and Certbot NGINX plugin:

apt install certbot python3-certbot-nginx

Once installed, run Certbot and follow on screen instructions:

certbot

7. Install Composer

Run these commands to install composer:

curl -sS https://getcomposer.org/installer -o composer-setup.php

HASH=$(curl -sS https://composer.github.io/installer.sig)

php -r "if (hash_file('SHA384', 'composer-setup.php') === '$HASH') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"

php composer-setup.php --install-dir=/usr/local/bin --filename=composer

Now confirm if the composer is installed with this command:

composer --version

8. Install Node + NPM via NVM

Run this command to install NVM (Node Version Manager):

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash

Now run nvm to install stable Node + NPM:

nvm install stable

If nvm doesn’t work, run these and try nvm install stable again:

export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

Conclusion

Setting up a server yourself can be daunting, especially if you’re new to DevOps. You can run into PHPMyAdmin not installing due to “Password does not satisfy the current policy requirements”, Conflicting Node/NPM versions etc. or so many other errors. So, over the years, I’ve perfected the solution that I wrote in this article. We installed PHP 8.3, NGINX, MySQL, PHPMyAdmin, SSL, Composer and NPM using the leanest way possible.

If you have any questions or run into issues, feel free to reach out. Happy coding!

Rizwan

About Rizwan

Full-stack developer and technical leader with 13+ years building scalable web applications. I help agencies and startups ship faster through strategic guidance and hands-on development.

MORE INSIGHTS

Keep reading for more development wisdom.

DevOps

How to Set Up a Secure Deploy User for GitHub Actions (And Why You Should Never Use Root)

Avoid root in Laravel CI/CD. Here’s how to create a secure "deploy" user for GitHub Actions, with exact commands and a full checklist.

Rizwan Rizwan July 13, 2025 5 min read
DevOps

How to Automatically Restart MySQL Database on Low Memory DigitalOcean Droplets

Safeguard your WordPress site on a low-memory DigitalOcean droplet by setting up an automatic MySQL monitoring and restart service to minimize downtime and keep your site running smoothly.

Rizwan Rizwan May 27, 2025 5 min read
DevOps

Redis Object Cache on OpenLiteSpeed: Halve WooCommerce TTFB

Learn how Redis object cache on OpenLiteSpeed can cut WooCommerce Time-to-First-Byte by up to 50%. Follow this step-by-step guide with real benchmarks.

Rizwan Rizwan May 27, 2025 5 min read

HOW I CAN HELP YOU

I work with founders, agencies, and developers who need that extra push to get projects live. Whether it's fixing a stubborn bug, steering your tech strategy, or building full apps with my team. You don't have to do it alone

GET UNSTUCK

60-minute call to debug your specific problem. Stop spinning your wheels.

$249
BOOK NOW →

FRACTIONAL CTO

Ongoing strategic guidance to prevent disasters like this.

$2k-7k/mo
LEARN MORE →

CUSTOM DEV

Full project rebuild with our expert team. Done right the first time.

Custom
GET QUOTE →