Install LEMP Stack on VPS Ubuntu: Nginx + MySQL + PHP-FPM
LEMP Stack is a collection of software for running Web Applications consisting of Linux, ENginx (pronounced "engine-x"), MySQL, and PHP-FPM. The key difference from LAMP Stack is the use of Nginx instead of Apache, which provides significantly better performance, especially under high traffic and when serving static files.
This article will guide you through installing LEMP Stack on Ubuntu 22.04 LTS VPS from start to finish, including basic security configuration.
Why Choose LEMP Over LAMP?
Nginx uses an Event-Driven, Non-Blocking I/O architecture, which can handle significantly more concurrent connections compared to Apache's Thread-Based Model. Additionally:
- Uses 30–50% less RAM on average compared to Apache
- Better suited for smaller VPS instances (1–2 GB RAM)
- Excellent reverse proxy capabilities for Node.js, Python, and other applications
- Seamless support for HTTP/2 and HTTP/3
Prerequisites
- VPS running Ubuntu 22.04 LTS (minimum 1 GB RAM recommended)
sudoprivileges or root access- A domain with A Record pointing to your VPS IP (for Virtual Host setup)
- SSH Client such as Terminal on macOS/Linux or PuTTY on Windows
Step 1: Update the System
After SSH-ing into your VPS, always update the package list and upgrade the system first:
sudo apt update && sudo apt upgrade -y
Wait for the process to complete, then reboot if there's a kernel update:
sudo reboot
Step 2: Install Nginx
Install Nginx from the official Ubuntu repository:
sudo apt install nginx -y
Enable it and start it on boot:
sudo systemctl enable nginx
sudo systemctl start nginx
Verify that Nginx is running correctly:
sudo systemctl status nginx
You should see a green active (running) message. Next, open ports 80 and 443 in the firewall:
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status
Step 3: Install MySQL
Install MySQL Server:
sudo apt install mysql-server -y
Run the secure installation script to set a root password and remove insecure settings:
sudo mysql_secure_installation
The system will ask a series of questions. We recommend answering as follows:
- VALIDATE PASSWORD component — Press
Yto enable it (recommended) - Password strength — Choose
2(STRONG) for production - Remove anonymous users —
Y - Disallow root login remotely —
Y - Remove test database —
Y - Reload privilege tables —
Y
Log in to MySQL to create a database and user for your website:
sudo mysql -u root -p
-- Create database
CREATE DATABASE mywebsite CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- Create user
CREATE USER 'webuser'@'localhost' IDENTIFIED BY 'StrongPassword123!';
-- Grant privileges
GRANT ALL PRIVILEGES ON mywebsite.* TO 'webuser'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Step 4: Install PHP-FPM
PHP-FPM (FastCGI Process Manager) is how Nginx runs PHP, passing requests through a Unix socket instead of loading PHP as a module like Apache does:
sudo apt install php8.1-fpm php8.1-mysql php8.1-curl php8.1-gd \
php8.1-mbstring php8.1-xml php8.1-zip php8.1-bcmath -y
Verify that PHP-FPM is running:
sudo systemctl status php8.1-fpm
Check the PHP version:
php -v
Step 5: Configure Nginx Virtual Host
Create a configuration file for your domain:
sudo nano /etc/nginx/sites-available/example.com
Paste the following content (replace example.com with your domain):
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com/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.1-fpm.sock;
}
location ~ /\.ht {
deny all;
}
# Cache Static Files
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
expires 30d;
add_header Cache-Control "public, no-transform";
}
}
Create the web root directory and test file:
sudo mkdir -p /var/www/example.com/html
sudo chown -R www-data:www-data /var/www/example.com
sudo chmod -R 755 /var/www/example.com
Enable the site and test the configuration:
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t
If you see syntax is ok and test is successful, reload Nginx:
sudo systemctl reload nginx
Step 6: Test PHP is Working
Create an info.php file to test:
sudo nano /var/www/example.com/html/info.php
<?php
phpinfo();
?>
Open your browser to http://example.com/info.php and you should see the PHP Info page displaying all configuration details.
info.php file after testing, as it exposes system information that could be a security vulnerability: sudo rm /var/www/example.com/html/info.phpStep 7: Install SSL with Let's Encrypt (Recommended)
Install Certbot:
sudo apt install certbot python3-certbot-nginx -y
Generate a certificate and automatically configure HTTPS:
sudo certbot --nginx -d example.com -d www.example.com
Certbot will ask for your email and agreement to the Terms of Service, then automatically modify your Nginx config to support HTTPS. The certificate will auto-renew every 90 days. Verify with:
sudo certbot renew --dry-run
Additional PHP-FPM Optimization
Edit PHP configuration for production:
sudo nano /etc/php/8.1/fpm/php.ini
Find and modify these values:
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 300
memory_limit = 256M
expose_php = Off
Restart PHP-FPM after making changes:
sudo systemctl restart php8.1-fpm
Additional Nginx Optimization
Edit /etc/nginx/nginx.conf to improve performance:
sudo nano /etc/nginx/nginx.conf
Important values to adjust based on your VPS CPU:
worker_processes auto; # Use all CPU cores automatically
worker_connections 1024; # Connections per worker
keepalive_timeout 65;
gzip on; # Enable Gzip compression
gzip_types text/plain text/css application/json application/javascript;
Summary: LEMP Stack Installation Checklist
After completing all steps, verify the following to ensure your system is ready:
- Nginx is running and displaying web pages over both HTTP and HTTPS
- MySQL is functional with a separate user for each database
- PHP-FPM is processing PHP scripts correctly
- The
info.phpfile has been deleted - SSL certificate is installed and auto-renewal is working
- Firewall (ufw) is open only for ports 22, 80, and 443
expose_php = Offis set in php.ini
/var/www/example.com/html/ directory. For WordPress installation specifically on LEMP Stack, you can follow our next article at 29vps.com.Verify LEMP Stack is Working Correctly
# Verify Nginx config has no syntax errors
sudo nginx -t
# Check status of all services
sudo systemctl status nginx php8.1-fpm mysql
# Test PHP is working through Nginx
echo "<?php echo phpversion(); ?>" | sudo tee /var/www/example.com/html/test.php
curl -s http://localhost/test.php
sudo rm /var/www/example.com/html/test.php
# Verify MySQL connection with your user
mysql -u webuser -p -e "SHOW DATABASES;" mywebsite
nginx -t displays both syntax is ok and test is successful at the same time, your config is definitely correct — no guessing needed.Common Issues & Troubleshooting
Error: 502 Bad Gateway After Setting Up PHP-FPM
This occurs when the PHP-FPM socket path in the Nginx config doesn't match the actual file.
# Check where the socket file actually is
ls /var/run/php/
# If you see php8.3-fpm.sock but config uses php8.1, edit it
sudo nano /etc/nginx/sites-available/example.com
# Change fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
# to the version that ls shows
sudo systemctl reload nginx
Error: 403 Forbidden When First Accessing the Website
No index.php / index.html file exists in the web root or permissions are incorrect.
# Check web root permissions
ls -la /var/www/example.com/html/
# Fix permissions
sudo chown -R www-data:www-data /var/www/example.com
sudo chmod -R 755 /var/www/example.com
# Create a test index.html file
echo "<h1>Hello LEMP</h1>" | sudo tee /var/www/example.com/html/index.html
PHP Code Not Being Parsed — Seeing Raw PHP Code in Browser
Nginx is not passing .php files to PHP-FPM. Check the location block in your config.
# Verify the location ~ \.php$ block exists in config
grep -n "\.php" /etc/nginx/sites-available/example.com
# Check if PHP-FPM is running
sudo systemctl status php8.1-fpm
# If stopped
sudo systemctl start php8.1-fpm
Certbot Cannot Request Certificate — Challenge Failed
Usually caused by DNS not pointing to your VPS or firewall blocking port 80.
# Verify A record points correctly
dig +short A example.com
# Check if port 80 is open
curl -I http://example.com
sudo ufw status | grep 80
MySQL: ERROR 1045 (28000): Access Denied for User
Password is incorrect or user doesn't have privileges on that database.
# Log in as root and check user
sudo mysql -u root
SELECT user, host FROM mysql.user WHERE user='webuser';
SHOW GRANTS FOR 'webuser'@'localhost';
-- If you need to reset password
ALTER USER 'webuser'@'localhost' IDENTIFIED BY 'NewPassword456!';
FLUSH PRIVILEGES;
Frequently Asked Questions
Can LEMP and LAMP both run WordPress the same way?
Yes, WordPress works the same on both. The main difference is that Nginx requires try_files directives instead of .htaccess that WordPress uses with Apache. Nginx doesn't read .htaccess files at all, so you must translate the rewrite rules into your nginx.conf instead.
How do I upgrade from PHP 8.1 to PHP 8.3?
You can install the new PHP version without removing the old one, then update the path in your Nginx config.
sudo apt install php8.3-fpm php8.3-mysql php8.3-curl php8.3-mbstring -y
# Edit Nginx config and change php8.1-fpm.sock → php8.3-fpm.sock
sudo systemctl reload nginx
Can I run Node.js or Python alongside LEMP after installation?
Yes, because Nginx can act as a reverse proxy. Use location blocks to direct traffic to PHP-FPM, Node.js, or Python apps in a single config file, with each backend running on its own port or socket.