Production LEMP Stack on Ubuntu 24.04: Nginx, PHP-FPM 8.3, MariaDB, Redis, SSL

Introduction
You just spun up a fresh Ubuntu 24.04 VPS. You need a web server that handles real traffic, serves PHP applications fast, caches queries, and runs HTTPS by default. This guide takes you from zero to a production-grade LEMP stack - Nginx, PHP-FPM 8.3, MariaDB, Redis, and automatic SSL via Let's Encrypt. Every command is tested. Every config is ready to copy-paste.
No fluff. No theory. Just a working server by the end of this page.
Prerequisites
- Ubuntu 24.04 LTS server (fresh install)
- Root or sudo access
- A domain name pointed to your server's IP (for SSL)
- SSH access
First, update your system:
apt update && apt upgrade -y
Step 1: Install Nginx
Nginx ships in Ubuntu's default repos. Install it directly:
apt install nginx -y
systemctl enable nginx --now
Verify it is running:
systemctl status nginx
curl -I http://localhost
You should see HTTP/1.1 200 OK. If your firewall is enabled, allow HTTP and HTTPS:
ufw allow 'Nginx Full'
Step 2: Install PHP-FPM 8.3
Ubuntu 24.04 includes PHP 8.3. Install it with the extensions most applications need:
apt install php8.3-fpm php8.3-cli php8.3-mysql php8.3-curl php8.3-gd \
php8.3-mbstring php8.3-xml php8.3-zip php8.3-bcmath php8.3-intl \
php8.3-redis php8.3-opcache -y
Start and enable:
systemctl enable php8.3-fpm --now
PHP-FPM runs as a socket by default at /run/php/php8.3-fpm.sock. Nginx will connect to this socket.
Configure PHP-FPM for Production
Edit /etc/php/8.3/fpm/pool.d/www.conf and set these values:
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 30
pm.max_requests = 500
These settings work for a 2-4 GB VPS. Adjust pm.max_children based on your memory: roughly 50 MB per child process.
Then restart:
systemctl restart php8.3-fpm
Step 3: Install MariaDB
MariaDB 10.11 is the default in Ubuntu 24.04:
apt install mariadb-server mariadb-client -y
systemctl enable mariadb --now
Secure the installation:
mysql_secure_installation
Answer yes to all prompts. Set a strong root password.
Create a database and user for your application:
mysql -u root -p
CREATE DATABASE app_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'strong-password-here';
GRANT ALL PRIVILEGES ON app_db.* TO 'app_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Step 4: Install and Configure Redis
Redis for object caching and session storage:
apt install redis-server -y
systemctl enable redis-server --now
Verify:
redis-cli ping
# Should return PONG
For production, edit /etc/redis/redis.conf:
maxmemory 256mb
maxmemory-policy allkeys-lru
Restart Redis:
systemctl restart redis-server
Step 5: Configure Nginx for PHP
Create a site configuration at /etc/nginx/sites-available/yourdomain.com:
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
root /var/www/yourdomain.com/public;
index index.php index.html;
# PHP handling
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_read_timeout 300;
}
# Cache static assets
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# Security: block access to dotfiles
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
}
Enable the site and test:
ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/
rm /etc/nginx/sites-enabled/default
nginx -t
systemctl reload nginx
Create your document root and a test PHP file:
mkdir -p /var/www/yourdomain.com/public
echo '<?php phpinfo(); ?>' > /var/www/yourdomain.com/public/index.php
Visit http://yourdomain.com. You should see the PHP info page. Delete the test file after verifying it works:
rm /var/www/yourdomain.com/public/index.php
Step 6: SSL with Let's Encrypt
Install Certbot:
apt install certbot python3-certbot-nginx -y
Obtain and install the certificate:
certbot --nginx -d yourdomain.com -d www.yourdomain.com
Certbot modifies your Nginx config to add SSL and sets up automatic renewal via a systemd timer. Verify renewal works:
certbot renew --dry-run
Your site is now running HTTPS with auto-renewing certificates.
Step 7: Optimize Nginx for Production
Edit /etc/nginx/nginx.conf and add inside the http block:
# Buffer size tuning
client_body_buffer_size 16k;
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
# Timeouts
client_body_timeout 12;
client_header_timeout 12;
keepalive_timeout 15;
send_timeout 10;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
# Limit requests
limit_req_zone $binary_remote_addr zone=login:10m rate=10r/m;
Reload Nginx:
systemctl reload nginx
Step 8: Install and Configure Fail2Ban
Protect against brute force attacks:
apt install fail2ban -y
Create /etc/fail2ban/jail.local:
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
[sshd]
enabled = true
[nginx-http-auth]
enabled = true
[nginx-botsearch]
enabled = true
Start Fail2Ban:
systemctl enable fail2ban --now
Verification Checklist
Run through these checks before going live:
systemctl status nginx php8.3-fpm mariadb redis-server- all activecurl -I https://yourdomain.com- returns 200 with valid SSLredis-cli ping- returns PONGmysql -u app_user -p -e "SELECT 1"- connects successfullynginx -t- configuration test passescertbot certificates- shows your certificate with expiry date
What This Stack Handles
This configuration comfortably serves 50-100 concurrent PHP requests on a 2 GB VPS. With Nginx micro-caching, Redis object cache, and MariaDB query cache, most WordPress, Laravel, and custom PHP applications run smoothly.
If you need higher throughput, add more PHP-FPM children (monitor with htop), increase Redis memory, or scale horizontally behind a load balancer.
How ServerGurus Helps
Every ServerGurus cloud VPS ships with Ubuntu 24.04 ready to go. Our bare metal servers give you dedicated CPU, NVMe storage, and unmetered bandwidth - which means this exact stack runs 10x faster than on a shared $5 instance. We also offer managed hosting where our team handles all the steps above for you, including ongoing security updates and monitoring.
Next Steps
- Deploy your application code to
/var/www/yourdomain.com/public - Set up nightly database backups:
mysqldump app_db | gzip > /backup/db-$(date +%F).sql.gz - Monitor resources with
htop,iotop, andnetdata - Set up log rotation in
/etc/logrotate.d/
For a VPS that runs this stack at production speed, check our plans. Need it managed for you? Talk to our team.