
How to Install Linux Nginx MySQL PHP (LEMP) Stack on Ubuntu
The LEMP stack is a powerful combination of Linux, Nginx, MySQL, and PHP that provides a robust foundation for web applications and dynamic websites. Unlike the more commonly discussed LAMP stack (which uses Apache), LEMP leverages Nginx’s superior performance for high-traffic scenarios and efficient resource utilization. This guide will walk you through the complete installation process on Ubuntu, covering everything from initial setup to configuration optimization, common troubleshooting scenarios, and performance tuning that you’ll actually need in production environments.
What is LEMP and How Does it Work
LEMP represents four key components working together to serve dynamic web content. Linux provides the operating system foundation, Nginx handles HTTP requests and serves static content with exceptional efficiency, MySQL manages database operations, and PHP processes server-side scripts. The architecture flow typically works like this: incoming requests hit Nginx first, which either serves static files directly or passes PHP requests to PHP-FPM (FastCGI Process Manager), which then communicates with MySQL when database operations are needed.
The real advantage of LEMP over LAMP comes down to Nginx’s event-driven architecture versus Apache’s process-based approach. While Apache creates a new thread or process for each connection, Nginx can handle thousands of concurrent connections with minimal memory overhead. This makes LEMP particularly effective for high-traffic applications, API endpoints, and scenarios where you need to serve many simultaneous users.
Component | LEMP Stack | LAMP Stack | Key Difference |
---|---|---|---|
Web Server | Nginx | Apache | Event-driven vs Process-based |
Memory Usage | ~2-4MB per worker | ~8-25MB per process | Nginx uses significantly less RAM |
Concurrent Connections | 10,000+ | 400-1000 | Nginx handles more simultaneous users |
Configuration | Simple config files | .htaccess support | Apache more flexible for shared hosting |
Prerequisites and System Preparation
Before diving into the installation, make sure you have a fresh Ubuntu server (18.04, 20.04, or 22.04 work well) with root access or sudo privileges. You’ll also want at least 1GB of RAM, though 2GB is recommended for production use. If you’re running this on a VPS, services like MangoHost VPS provide pre-configured Ubuntu instances that work perfectly for LEMP setups.
First, update your system packages:
sudo apt update && sudo apt upgrade -y
Install some essential packages that you’ll likely need:
sudo apt install curl wget gnupg2 software-properties-common apt-transport-https ca-certificates lsb-release
Installing Nginx Web Server
Nginx installation on Ubuntu is straightforward, but there are a few approaches depending on whether you want the stable repository version or the latest mainline release. For most production scenarios, the stable version works perfectly:
sudo apt install nginx -y
Start and enable Nginx to run automatically on boot:
sudo systemctl start nginx
sudo systemctl enable nginx
Verify the installation by checking the status:
sudo systemctl status nginx
You should see output indicating Nginx is active and running. Test it by visiting your server’s IP address in a browser – you’ll see the default Nginx welcome page.
For those who need the latest features, you can add the official Nginx repository:
curl -fsSL https://nginx.org/keys/nginx_signing.key | sudo apt-key add -
sudo add-apt-repository "deb http://nginx.org/packages/ubuntu $(lsb_release -cs) nginx"
sudo apt update
sudo apt install nginx -y
The key configuration files you’ll work with are:
/etc/nginx/nginx.conf
– Main configuration file/etc/nginx/sites-available/
– Site configuration files/etc/nginx/sites-enabled/
– Symlinks to active sites/var/log/nginx/
– Access and error logs
Installing MySQL Database Server
MySQL installation has become slightly more complex since Ubuntu started including MySQL 8.0 by default, which uses a different authentication method. Here’s how to install and configure it properly:
sudo apt install mysql-server -y
Run the security installation script to set up basic security measures:
sudo mysql_secure_installation
This script will ask several questions. Here are the recommended responses:
- Set up VALIDATE PASSWORD plugin: Yes (choose strength level based on your needs)
- Set root password: Yes (use a strong password)
- Remove anonymous users: Yes
- Disallow root login remotely: Yes (unless you specifically need remote root access)
- Remove test database: Yes
- Reload privilege tables: Yes
MySQL 8.0 uses caching_sha2_password
authentication by default, which can cause issues with some PHP applications. If you encounter authentication problems, you might need to create a user with the older authentication method:
sudo mysql -u root -p
Then in the MySQL prompt:
CREATE USER 'your_user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_password';
GRANT ALL PRIVILEGES ON *.* TO 'your_user'@'localhost' WITH GRANT OPTION;
FLUSH PRIVILEGES;
EXIT;
Check MySQL status to ensure it’s running:
sudo systemctl status mysql
Installing PHP and PHP-FPM
PHP installation involves several components. You’ll need PHP itself, PHP-FPM (FastCGI Process Manager) to interface with Nginx, and various PHP extensions for common functionality:
sudo apt install php php-fpm php-mysql php-json php-curl php-gd php-xml php-mbstring php-zip php-intl php-bcmath -y
The specific PHP version installed depends on your Ubuntu version. Ubuntu 20.04 installs PHP 7.4, while 22.04 installs PHP 8.1. You can check your PHP version:
php -v
Start and enable PHP-FPM:
sudo systemctl start php8.1-fpm # Replace 8.1 with your PHP version
sudo systemctl enable php8.1-fpm
PHP-FPM configuration files are located at:
/etc/php/8.1/fpm/php.ini
– Main PHP configuration/etc/php/8.1/fpm/pool.d/www.conf
– PHP-FPM pool configuration/var/log/php8.1-fpm.log
– PHP-FPM logs
For production environments, you’ll want to adjust some PHP settings. Edit the php.ini file:
sudo nano /etc/php/8.1/fpm/php.ini
Key settings to consider:
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 300
max_input_vars = 3000
memory_limit = 256M
After making changes, restart PHP-FPM:
sudo systemctl restart php8.1-fpm
Configuring Nginx to Work with PHP
This is where many people run into issues. Nginx doesn’t have built-in PHP support like Apache’s mod_php, so you need to configure it to pass PHP requests to PHP-FPM. Create a new server block configuration:
sudo nano /etc/nginx/sites-available/your-domain.com
Here’s a complete, production-ready configuration:
server {
listen 80;
server_name your-domain.com www.your-domain.com;
root /var/www/your-domain.com;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.ht {
deny all;
}
# Optional: Enable gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
}
Create the document root directory:
sudo mkdir -p /var/www/your-domain.com
sudo chown -R www-data:www-data /var/www/your-domain.com
sudo chmod -R 755 /var/www/your-domain.com
Enable the site by creating a symbolic link:
sudo ln -s /etc/nginx/sites-available/your-domain.com /etc/nginx/sites-enabled/
Test the Nginx configuration:
sudo nginx -t
If the test passes, reload Nginx:
sudo systemctl reload nginx
Testing Your LEMP Stack
Create a PHP info file to test the entire stack:
sudo nano /var/www/your-domain.com/info.php
Add this content:
<?php
phpinfo();
?>
Visit http://your-domain.com/info.php
in your browser. You should see the PHP information page showing PHP version, loaded modules, and configuration details.
Test MySQL connectivity with a simple PHP script:
sudo nano /var/www/your-domain.com/test-db.php
<?php
$servername = "localhost";
$username = "your_user";
$password = "your_password";
try {
$pdo = new PDO("mysql:host=$servername", $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo "Connected successfully to MySQL";
} catch(PDOException $e) {
echo "Connection failed: " . $e->getMessage();
}
?>
Remember to delete these test files once you’ve verified everything works:
sudo rm /var/www/your-domain.com/info.php /var/www/your-domain.com/test-db.php
Performance Optimization and Best Practices
A basic LEMP installation works, but production environments need optimization. Here are configurations that actually make a difference:
First, optimize Nginx worker processes. Edit the main configuration:
sudo nano /etc/nginx/nginx.conf
Key optimizations:
worker_processes auto;
worker_connections 1024;
keepalive_timeout 65;
client_max_body_size 64m;
# Add in http block
gzip on;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
# Enable caching for static files
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 1M;
add_header Cache-Control "public, immutable";
}
Optimize PHP-FPM pool settings based on your server’s RAM:
sudo nano /etc/php/8.1/fpm/pool.d/www.conf
For a server with 2GB RAM:
pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
pm.max_requests = 500
MySQL optimization for typical web applications:
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
Add these settings under [mysqld]:
innodb_buffer_pool_size = 512M # 25-75% of available RAM
query_cache_type = 1
query_cache_size = 64M
max_connections = 151
thread_cache_size = 8
Restart all services after optimization:
sudo systemctl restart nginx php8.1-fpm mysql
Common Issues and Troubleshooting
Several issues consistently trip up LEMP installations. Here’s how to diagnose and fix the most common ones:
502 Bad Gateway Error: This usually means Nginx can’t communicate with PHP-FPM. Check if PHP-FPM is running:
sudo systemctl status php8.1-fpm
Verify the socket path in your Nginx configuration matches the PHP-FPM configuration:
grep "listen =" /etc/php/8.1/fpm/pool.d/www.conf
Permission Denied Errors: Usually caused by incorrect file ownership or permissions:
sudo chown -R www-data:www-data /var/www/your-domain.com
sudo find /var/www/your-domain.com -type d -exec chmod 755 {} \;
sudo find /var/www/your-domain.com -type f -exec chmod 644 {} \;
PHP Files Download Instead of Execute: Nginx isn’t configured to process PHP files. Double-check your location block for PHP processing and ensure PHP-FPM is running.
MySQL Connection Refused: Check if MySQL is running and listening on the correct port:
sudo systemctl status mysql
sudo netstat -tlnp | grep mysql
Useful log files for troubleshooting:
/var/log/nginx/error.log
– Nginx errors/var/log/php8.1-fpm.log
– PHP-FPM errors/var/log/mysql/error.log
– MySQL errors/var/log/syslog
– System-wide logs
Security Considerations
A default LEMP installation isn’t production-ready from a security perspective. Implement these essential security measures:
Configure a firewall using UFW:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
Hide Nginx and PHP version information:
# In /etc/nginx/nginx.conf, add to http block:
server_tokens off;
# In /etc/php/8.1/fpm/php.ini:
expose_php = Off
Disable dangerous PHP functions:
# In /etc/php/8.1/fpm/php.ini:
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source
Set up SSL certificates using Let’s Encrypt:
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d your-domain.com -d www.your-domain.com
Real-World Use Cases and Applications
LEMP stacks excel in several scenarios where performance and resource efficiency matter. E-commerce platforms like WooCommerce or Magento benefit significantly from Nginx’s ability to handle concurrent connections and serve static assets efficiently. Content management systems including WordPress, Drupal, and custom PHP applications see improved response times, especially under traffic spikes.
API development is another sweet spot for LEMP. If you’re building REST APIs or microservices with PHP frameworks like Laravel, Symfony, or Slim, the low memory footprint of Nginx means you can run more application instances on the same hardware compared to Apache-based setups.
For high-traffic scenarios, LEMP configurations have been proven to handle 10,000+ concurrent connections on modest hardware. A typical 4GB RAM server can comfortably serve 50-100 requests per second with proper optimization, making it cost-effective for growing applications.
Application Type | LEMP Advantages | Typical Performance |
---|---|---|
WordPress Sites | Fast static file serving, efficient PHP processing | 2-3x faster than LAMP |
E-commerce | Handles traffic spikes, low resource usage | 500+ concurrent users on 2GB RAM |
API Endpoints | Lightweight, fast JSON responses | 1000+ requests/sec on optimized setup |
SaaS Applications | Scalable, efficient resource utilization | Scales horizontally with load balancing |
If you’re planning to scale beyond a single server, LEMP stacks work exceptionally well with load balancers and can be easily deployed across multiple servers. For businesses requiring dedicated resources, dedicated server solutions provide the foundation for high-performance LEMP deployments that can handle enterprise-level traffic.
The combination of Nginx’s reverse proxy capabilities with PHP-FPM’s process management makes LEMP particularly suitable for modern web applications that need to integrate with external APIs, handle file uploads, or serve mixed content types efficiently.

This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.
This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.