Setting Up an LNMP / LAMP Stack End to End

Manually build an Nginx- or Apache-based PHP stack on your server with MySQL, then confirm PHP actually executes.

LNMP and LAMP are the two classic back-end combos for running PHP sites (WordPress, Laravel, and friends). Once you know what each letter means and have installed the stack by hand, you own your hosting environment instead of renting a black box.

What each letter stands for

  • L = Linux — the operating system. This guide targets Ubuntu/Debian.
  • N = Nginx or A = Apache — the web server that accepts HTTP requests. Nginx is lean and excels at static files and high concurrency; Apache is flexible and has a mature .htaccess ecosystem.
  • M = MySQL (or the drop-in MariaDB) — the database.
  • P = PHP — the scripting language. With Nginx you pair it with PHP-FPM (a standalone process pool); with Apache you typically embed it via libapache2-mod-php.

The only real difference is the web server. Pick one — don't run both on port 80.

Prep

Refresh your package index first:

sudo apt update && sudo apt upgrade -y

Option A: LNMP (Nginx + PHP-FPM)

sudo apt install -y nginx mysql-server php-fpm php-mysql

Nginx now listens on port 80. Edit the site config at /etc/nginx/sites-available/default and add PHP handling inside the server block (mind the version, e.g. 8.3):

index index.php index.html;

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

Test and reload:

sudo nginx -t && sudo systemctl reload nginx

Option B: LAMP (Apache + mod-php)

sudo apt install -y apache2 mysql-server php libapache2-mod-php php-mysql
sudo systemctl restart apache2

Apache loads the PHP module out of the box, so .php files execute with no extra config.

Initialize the database

Run the hardening script to set a root password and drop anonymous accounts:

sudo mysql_secure_installation

Create a dedicated database and user for each app — never ship with root:

CREATE DATABASE myapp CHARACTER SET utf8mb4;
CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'a-strong-password';
GRANT ALL ON myapp.* TO 'appuser'@'localhost';
FLUSH PRIVILEGES;

Verify PHP execution

Drop a test file into the web root (Nginx defaults to /var/www/html):

echo "<?php phpinfo(); ?>" | sudo tee /var/www/html/info.php

Visit http://your-server-ip/info.php. A PHP info table means success. If the browser downloads the file or shows raw source, PHP isn't being handled — recheck the FPM socket path, the version number, and whether the service is running. Delete the file immediately afterward so you don't leak environment details:

sudo rm /var/www/html/info.php

CentOS / Rocky differences

Use dnf, expect the PHP socket at /run/php-fpm/www.sock, and open the firewall:

sudo dnf install -y nginx mysql-server php-fpm php-mysqlnd
sudo firewall-cmd --add-service=http --permanent && sudo firewall-cmd --reload

Manual install vs one-click / control panels

  • One-click bundles / control panels: a GUI gives you a working stack in minutes — great for beginners and quick launches. The trade-off is panel-locked versions, extra background services, and a more opaque box when something breaks.
  • Manual apt install: you control every version, path, and setting, with minimal dependencies and a setup close to production. The cost is writing the config yourself.

For learning and small projects, install by hand at least once. Panels are fine for convenience — but understand the manual flow first, so you know where to look when things go wrong.

Summary

Choosing Nginx or Apache only changes the web-server layer; MySQL and PHP stay the same. The core loop is: install the packages, configure the web server to hand .php to PHP, initialize the database, then confirm with phpinfo() and delete the test file. Once the manual path is second nature, a control panel becomes a convenience — not a mystery.