Install LEMP (Nginx, MySQL, PHP) on Ubuntu 24.04

LEMP is the Linux, Nginx, MySQL and PHP stack behind WordPress, Laravel and most other PHP applications. This guide is written for Ubuntu 24.04 LTS and also works on 22.04 and 26.04: only the PHP version number differs.
What you need
- a VPS running Ubuntu with SSH access;
- a user with
sudorights; - a domain with an A record pointing to the server’s IP address (for SSL).
Update the system:
sudo apt update && sudo apt upgrade -y
Step 1. Nginx
sudo apt install -y nginx
If UFW is enabled, allow HTTP and HTTPS:
sudo ufw allow 'Nginx Full'
Open http://your-server-ip in a browser — you should see the “Welcome to nginx” page.
Step 2. MySQL
sudo apt install -y mysql-server
Run the basic hardening script. It removes anonymous users and the test database and disables remote root login:
sudo mysql_secure_installation
On Ubuntu the root user logs in to MySQL without a password, through sudo. Create a database and a dedicated user for the site:
sudo mysql
CREATE DATABASE site_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'site_user'@'localhost' IDENTIFIED BY 'a_strong_password';
GRANT ALL PRIVILEGES ON site_db.* TO 'site_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Step 3. PHP
Nginx does not run PHP itself; it hands requests to the PHP-FPM service:
sudo apt install -y php-fpm php-mysql php-curl php-gd php-mbstring php-xml php-zip
Find the installed version and the socket name — you need them in the next step:
php -v
ls /run/php/
On Ubuntu 24.04 that is PHP 8.3 and the php8.3-fpm.sock socket.
Step 4. Configure the site
Create the site directory:
sudo mkdir -p /var/www/example.com
sudo chown -R $USER:www-data /var/www/example.com
Create the configuration:
sudo nano /etc/nginx/sites-available/example.com
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
location ~ /\.ht {
deny all;
}
}
Enable the site, test the configuration and reload it:
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
The configuration is explained in detail in the Nginx virtual host guide.
Step 5. Test PHP
echo "<?php phpinfo();" > /var/www/example.com/info.php
Open http://example.com/info.php — a page with PHP settings appears.
Step 6. Test the database connection
Create /var/www/example.com/db.php:
<?php
$db = new mysqli('localhost', 'site_user', 'a_strong_password', 'site_db');
echo $db->connect_error ? 'Error: ' . $db->connect_error : 'MySQL connection works';
Open http://example.com/db.php, confirm the connection works, and delete the file.
Step 7. SSL certificate
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
Certbot obtains a Let’s Encrypt certificate and sets up HTTPS and the HTTP redirect. Renewal is automatic.
Basic PHP tuning
The settings are in /etc/php/8.3/fpm/php.ini. Most sites benefit from higher upload limits:
upload_max_filesize = 64M
post_max_size = 64M
memory_limit = 256M
For Nginx to accept such files, add client_max_body_size 64M; to the server block. Then restart the services:
sudo systemctl restart php8.3-fpm
sudo systemctl reload nginx
If something does not work
- 502 Bad Gateway — the Nginx configuration points to the wrong PHP-FPM socket, or the service is not running:
systemctl status php8.3-fpm. - The browser downloads the PHP file — the site configuration has no
location ~ \.php$block. - Access denied for user — check the user name, password and database privileges.
- Logs —
/var/log/nginx/error.logandjournalctl -u php8.3-fpm.


