How to Install LEMP on Debian

The LEMP stack combines Linux, Nginx, MariaDB, and PHP to create a high-performance foundation for hosting dynamic web applications. Whether you need to run WordPress, Laravel, Drupal, or custom PHP projects, this combination provides the speed and flexibility required for production workloads.

This guide walks you through installing and configuring each component on Debian. By the end, you will have Nginx serving web requests, MariaDB handling database storage, and PHP-FPM processing dynamic content. Additionally, you will secure your setup with a free SSL certificate from Letโ€™s Encrypt and configure firewall rules to protect your server.

Understanding LEMP Stack Components

Before diving into the installation, however, understanding each component helps you troubleshoot issues and optimize performance:

  • Nginx: A high-performance HTTP server and reverse proxy designed for efficiency. Unlike Apache, Nginx uses an event-driven architecture that handles thousands of concurrent connections with minimal memory overhead. This makes it ideal for high-traffic websites and API endpoints.
  • MariaDB: A community-developed fork of MySQL that maintains compatibility while adding features like improved replication and storage engines. Debian includes MariaDB in its default repositories as the primary MySQL-compatible database server.
  • PHP-FPM: The FastCGI Process Manager for PHP enables Nginx to execute PHP scripts efficiently. PHP-FPM manages worker processes that handle PHP requests, offering features like adaptive process spawning and separate pool configurations for different sites.

Default Versions by Debian Release

Debian includes specific versions of each LEMP component in its default repositories. The following table shows what you get with each supported release:

ComponentDebian 13 (Trixie)Debian 12 (Bookworm)Debian 11 (Bullseye)
Nginx1.26.x1.22.x1.18.x
MariaDB11.8.x10.11.x10.5.x
PHP8.4.x8.2.x7.4.x

Most commands in this guide work identically across Debian 11, 12, and 13. Where differences exist (primarily PHP-FPM service names and socket paths), the guide provides version-specific instructions. Version numbers shown in expected outputs are examples; your output will reflect the actual version included in your Debian release.

LEMP Part 1: Install Nginx on Debian

Step 1: Update Your Debian System

Before installing any components, update your package lists and upgrade installed packages to their latest versions. This ensures you install the most recent stable versions of all LEMP components and prevents dependency conflicts:

sudo apt update && sudo apt upgrade

Specifically, apt update refreshes the package index from configured repositories, while apt upgrade installs available updates for all installed packages.

Step 2: Install Nginx

Subsequently, install Nginx from Debianโ€™s default repositories:

sudo apt install nginx

Once installation completes, Nginx starts automatically. As a result, you can confirm the service is running by checking its status:

sudo systemctl status nginx

Typically, a healthy installation displays output similar to the following:

โ— nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: active (running) since Mon 2025-01-01 00:00:00 UTC; 1min ago
       Docs: man:nginx(8)
   Main PID: 1236 (nginx)
      Tasks: 2 (limit: 4915)
     Memory: 3.5M
        CPU: 25ms
     CGroup: /system.slice/nginx.service
             โ”œโ”€1236 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
             โ””โ”€1237 "nginx: worker process"

However, if Nginx is not running, start the service and enable it to launch at boot:

sudo systemctl enable nginx --now

Step 3: Verify Nginx Installation

Consequently, to confirm Nginx is serving pages correctly, check the installed version:

nginx -v

This command displays the version, which varies depending on your Debian release (1.18.x on Debian 11, 1.22.x on Debian 12, or 1.26.x on Debian 13):

nginx version: nginx/1.x.x

Additionally, you can open your serverโ€™s IP address in a web browser. You should see the default Nginx welcome page confirming the web server is accessible.

For users who need the latest Nginx features or security patches, refer to our guide on installing Nginx Mainline on Debian, which covers adding the official Nginx repository. For configuration details, see the official Nginx documentation.

LEMP Part 2: Install MariaDB on Debian

Now that Nginx is running, the next component is MariaDB, which provides the database layer for your LEMP stack. Because Debian includes MariaDB in its default repositories, installation is straightforward.

If you need a specific MariaDB version or features from the official MariaDB repository, see our dedicated guide on installing MariaDB on Debian.

Step 1: Install MariaDB Server

Initially, install both the MariaDB server and client packages:

sudo apt install mariadb-server mariadb-client

Step 2: Verify MariaDB Service Status

Following the installation, verify that MariaDB is running:

sudo systemctl status mariadb

As expected, the status output confirms whether MariaDB is active:

โ— mariadb.service - MariaDB x.x.x database server
     Loaded: loaded (/lib/systemd/system/mariadb.service; enabled; preset: enabled)
     Active: active (running) since Sat 2025-01-01 00:00:00 UTC; 30s ago
       Docs: man:mariadbd(8)
             https://mariadb.com/kb/en/library/systemd/
   Main PID: 2345 (mariadbd)
     Status: "Taking your SQL requests now..."
      Tasks: 9 (limit: 4915)
     Memory: 80.0M
        CPU: 500ms
     CGroup: /system.slice/mariadb.service
             โ””โ”€2345 /usr/sbin/mariadbd

Should MariaDB not be running, start and enable it with this command:

sudo systemctl enable mariadb --now

Step 3: Secure the MariaDB Installation

MariaDB includes a security script that hardens the default configuration by removing test databases, anonymous users, and restricting root access. Therefore, run this script immediately after installation:

sudo mysql_secure_installation

The script prompts you for several security options. Follow these recommendations:

  • Enter current password for root: Press Enter on a fresh installation (no password is set by default).
  • Switch to unix_socket authentication: Type Y and press Enter. This ensures only system users with appropriate permissions can access the MariaDB root account.
  • Change the root password: Type Y and enter a strong password if you want password-based access in addition to socket authentication.
  • Remove anonymous users: Type Y and press Enter. Anonymous users can pose security risks in production environments.
  • Disallow root login remotely: Type Y and press Enter. This prevents unauthorized remote access attempts targeting the root account.
  • Remove test database: Type Y and press Enter. The test database is accessible to all users and serves no purpose in production.
  • Reload privilege tables: Type Y and press Enter to apply all changes immediately.

Step 4: Verify MariaDB Version

To conclude the MariaDB setup, confirm the installation by checking the version:

mariadb --version

This displays version information that varies by Debian release (10.5.x on Debian 11, 10.11.x on Debian 12, or 11.8.x on Debian 13):

mariadb from x.x.x-MariaDB, client 15.2 for debian-linux-gnu (x86_64) using EditLine wrapper

For a graphical interface to manage your databases, see our guide on installing phpMyAdmin with Nginx on Debian. For advanced configuration, refer to the official MariaDB documentation.

LEMP Part 3: Install PHP on Debian

At this point, PHP processes dynamic content and connects Nginx to MariaDB. You will install PHP-FPM (FastCGI Process Manager) along with common extensions required by most web applications.

Debian includes a specific PHP version in each release. If you need a different PHP version, see our guide on installing PHP on Debian for instructions on adding the Sury repository.

Step 1: Install PHP-FPM and Extensions

To begin the process, install PHP-FPM and commonly required extensions:

sudo apt install php-fpm php php-cli php-mysql php-curl php-gd php-mbstring php-xml php-zip

This command installs the PHP-FPM meta-package, which automatically pulls in the correct PHP version for your Debian release (PHP 7.4 on Debian 11, PHP 8.2 on Debian 12, or PHP 8.4 on Debian 13). The additional extensions provide essential functionality:

  • php-gd: Image processing for thumbnails and uploads
  • php-mbstring: Multibyte string handling for international character sets
  • php-xml: XML parsing required by most CMS platforms
  • php-zip: Archive handling for plugin and theme installations

WordPress, Drupal, and Laravel may require additional extensions like php-intl (internationalization) or php-imagick (advanced image processing). Install them as needed with sudo apt install php-intl php-imagick.

Step 2: Verify PHP-FPM Service Status

Importantly, PHP-FPM runs as a version-specific service. To proceed, first identify your installed PHP version:

php -v

The output shows your installed PHP version, which will match your Debian release:

PHP 8.x.x (cli) (built: Mon DD YYYY HH:MM:SS) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.x.x, Copyright (c) Zend Technologies
    with Zend OPcache v8.x.x, Copyright (c), by Zend Technologies

Debian 11 ships PHP 7.4, Debian 12 ships PHP 8.2, and Debian 13 ships PHP 8.4. Your output will show the version matching your release.

Next, check the PHP-FPM service status using your version number. Replace 8.4 with your actual PHP version:

For Debian 13 (PHP 8.4):

sudo systemctl status php8.4-fpm

On Debian 12 (PHP 8.2):

sudo systemctl status php8.2-fpm

With Debian 11 (PHP 7.4):

sudo systemctl status php7.4-fpm

Consequently, a running service displays status information similar to this (service name reflects your PHP version):

โ— phpX.X-fpm.service - The PHP X.X FastCGI Process Manager
     Loaded: loaded (/lib/systemd/system/phpX.X-fpm.service; enabled; preset: enabled)
     Active: active (running) since Sat 2025-01-01 00:00:00 UTC; 1min ago
       Docs: man:php-fpmX.X(8)
   Main PID: 3450 (php-fpmX.X)
     Status: "Processes active: 0, idle: 2, Requests: 0, slow: 0, Traffic: 0req/sec"
      Tasks: 3 (limit: 4915)
     Memory: 8.0M
        CPU: 50ms
     CGroup: /system.slice/phpX.X-fpm.service
             โ”œโ”€3450 "php-fpm: master process (/etc/php/X.X/fpm/php-fpm.conf)"
             โ”œโ”€3451 "php-fpm: pool www"
             โ””โ”€3452 "php-fpm: pool www"

Step 3: Enable PHP-FPM Service

If PHP-FPM is not running, you need to enable and start it. Therefore, choose the command matching your PHP version:

For Debian 13 (PHP 8.4):

sudo systemctl enable php8.4-fpm --now

On Debian 12 (PHP 8.2):

sudo systemctl enable php8.2-fpm --now

With Debian 11 (PHP 7.4):

sudo systemctl enable php7.4-fpm --now

Configure Nginx to Process PHP Files

By default, however, Nginx does not process PHP files. You must configure a server block that passes PHP requests to PHP-FPM via a Unix socket.

Step 1: Create a Server Block Configuration

To initiate the configuration, create a new server block configuration file for your domain. Replace example.com with your actual domain:

sudo nano /etc/nginx/sites-available/example.com.conf

Next, add the following configuration. Adjust the PHP socket path to match your PHP version (the example uses PHP 8.4 for Debian 13):

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    root /var/www/html/example.com;
    index index.php index.html index.htm;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        # PHP 8.4 socket path for Debian 13
        # Debian 12: /run/php/php8.2-fpm.sock
        # Debian 11: /run/php/php7.4-fpm.sock
        fastcgi_pass unix:/run/php/php8.4-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
        fastcgi_buffer_size 128k;
        fastcgi_buffers 4 128k;
        fastcgi_intercept_errors on;
    }

    location ~ /\.ht {
        deny all;
    }
}

The fastcgi_pass directive must point to the correct PHP-FPM socket for your Debian version. Use /run/php/php8.4-fpm.sock for Debian 13, /run/php/php8.2-fpm.sock for Debian 12, or /run/php/php7.4-fpm.sock for Debian 11.

Save the file by pressing Ctrl+O, then exit with Ctrl+X.

Step 2: Create the Web Root Directory

First and foremost, create the document root directory specified in your server block:

sudo mkdir -p /var/www/html/example.com

Subsequently, set appropriate ownership so your user account can manage files:

sudo chown -R $USER:$USER /var/www/html/example.com

Thereafter, set permissions to allow Nginx to read files:

sudo chmod -R 755 /var/www/html/example.com

Step 3: Enable the Server Block

At this stage, create a symbolic link from sites-available to sites-enabled to activate your configuration:

sudo ln -s /etc/nginx/sites-available/example.com.conf /etc/nginx/sites-enabled/

Step 4: Test and Reload Nginx

Prior to applying changes, always test the Nginx configuration for syntax errors:

sudo nginx -t

If successful, the test produces this output:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

If the test passes, reload Nginx to apply your changes:

sudo systemctl reload nginx

Create a PHP Test Page

With all components configured, you should now verify that PHP is processing correctly by creating a test page that displays PHP configuration information.

Step 1: Create a PHP Info File

First, use the following command to create a test PHP file in your document root:

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

Step 2: Test PHP Processing

Then, open your browser and navigate to http://example.com/info.php (or http://your-server-ip/info.php if you have not configured DNS). You should see a detailed PHP information page showing your PHP version, loaded modules, and configuration settings.

Step 3: Remove the Test File

After confirming PHP works correctly, remove the info.php file. Leaving it accessible exposes sensitive server configuration details:

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

For a complete WordPress installation using your new LEMP stack, see our guide on installing WordPress with Nginx on Debian.

Verify Complete LEMP Stack

Before proceeding to SSL configuration, you should verify that all LEMP components are running and properly configured. This quick check confirms everything works together.

Check All Services

First, verify that Nginx, MariaDB, and PHP-FPM are all running. Use the PHP-FPM service name matching your Debian version:

For Debian 13:

sudo systemctl is-active nginx mariadb php8.4-fpm

On Debian 12:

sudo systemctl is-active nginx mariadb php8.2-fpm

With Debian 11:

sudo systemctl is-active nginx mariadb php7.4-fpm

Importantly, a healthy stack returns active for all three services:

active
active
active

Test PHP and MariaDB Connection

To test the PHP-to-MariaDB connection, first create a temporary database user that PHP can use. Connect to MariaDB as root:

sudo mariadb

At the MariaDB prompt, create a test user and grant minimal privileges:

CREATE USER 'testuser'@'localhost' IDENTIFIED BY 'testpass';
GRANT USAGE ON *.* TO 'testuser'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Now create a PHP file that tests the connection:

sudo tee /var/www/html/example.com/test-db.php <<'EOF'
<?php
$conn = new mysqli("localhost", "testuser", "testpass");
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}
echo "Connected to MariaDB successfully. Server version: " . $conn->server_info;
$conn->close();
?>
EOF

Visit http://example.com/test-db.php in your browser. A successful connection displays the MariaDB version. After testing, remove the file and test user:

sudo rm /var/www/html/example.com/test-db.php
sudo mariadb -e "DROP USER 'testuser'@'localhost';"

With unix_socket authentication enabled (recommended in this guide), the MariaDB root account can only be accessed by system users with root privileges. PHP running as www-data cannot connect as MariaDB root, which is why this test uses a dedicated test user.

Secure Your LEMP Stack with Letโ€™s Encrypt SSL

Implementing an SSL certificate encrypts traffic between your server and visitors. This improves security and is required for modern browsers to display your site without security warnings. Letโ€™s Encrypt provides free, automated SSL certificates.

For advanced SSL configuration options, wildcard certificates, and troubleshooting, see our detailed guide on securing Nginx with Letโ€™s Encrypt on Debian.

Step 1: Install Certbot

As a first step, install Certbot and its Nginx plugin:

sudo apt install python3-certbot-nginx -y

Step 2: Obtain and Install SSL Certificate

Afterward, run Certbot with the Nginx plugin. Replace the email address and domain with your own:

sudo certbot --nginx --agree-tos --redirect --hsts --staple-ocsp --email you@example.com -d example.com -d www.example.com

In particular, this command performs several security-enhancing actions automatically:

  • --nginx: Uses the Nginx plugin to automatically configure your server block for SSL.
  • --agree-tos: Accepts Letโ€™s Encryptโ€™s terms of service.
  • --redirect: Adds a 301 redirect from HTTP to HTTPS, ensuring all visitors use the encrypted connection.
  • --hsts: Enables HTTP Strict Transport Security, instructing browsers to only connect via HTTPS.
  • --staple-ocsp: Enables OCSP stapling, which speeds up SSL verification by having your server provide certificate validity proof directly.

Once Certbot completes, your site is accessible via https://example.com. Additionally, Certbot automatically renews certificates before expiration.

Configure Firewall Rules

Assuming your server runs a firewall, you must allow HTTP and HTTPS traffic. The following examples use UFW (Uncomplicated Firewall), which is commonly used on Debian systems.

Step 1: Install and Enable UFW

In the event that UFW is not installed, install it:

sudo apt install ufw

Important: If you are connected via SSH, allow SSH access before enabling UFW to prevent locking yourself out of your server.

sudo ufw allow ssh

Step 2: Allow HTTP and HTTPS Traffic

Conveniently, Nginx registers application profiles with UFW. Allow both HTTP and HTTPS:

sudo ufw allow 'Nginx Full'

Alternatively, however, you can allow ports individually:

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

Step 3: Enable UFW

Finally, enable the firewall:

sudo ufw enable

Then verify the firewall status:

sudo ufw status

The firewall status displays your configured rules:

Status: active

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW       Anywhere
Nginx Full                 ALLOW       Anywhere
22/tcp (v6)                ALLOW       Anywhere (v6)
Nginx Full (v6)            ALLOW       Anywhere (v6)

Troubleshooting Common LEMP Issues

When troubleshooting LEMP issues, log files are your primary diagnostic tool. As such, each component writes logs to specific locations:

  • Nginx logs: /var/log/nginx/error.log and /var/log/nginx/access.log
  • PHP-FPM logs: /var/log/php8.4-fpm.log (replace 8.4 with your PHP version)
  • MariaDB logs: /var/log/mysql/error.log

For example, view recent errors with sudo tail -50 /var/log/nginx/error.log to quickly identify issues.

Nginx Fails to Start Due to Port Conflict

If Nginx fails to start, another service may be using port 80. To diagnose this, install lsof if it is not already available (minimal Debian installations often omit it):

sudo apt install lsof

With the tool installed, check for port conflicts:

sudo lsof -i :80

For example, if Apache is using port 80, the output looks like this:

COMMAND  PID     USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
apache2 1234     root    4u  IPv6  12345      0t0  TCP *:http (LISTEN)

To resolve this conflict, first stop the conflicting service, then start Nginx:

sudo systemctl stop apache2
sudo systemctl start nginx

PHP Files Download Instead of Executing

When your browser downloads PHP files instead of displaying them, the PHP location block is likely missing or misconfigured. In this case, verify your server block includes the PHP processing section and that the socket path matches your PHP version.

In addition to this, check that the PHP-FPM socket exists:

ls -la /run/php/

When correctly configured, the installation shows a socket file matching your PHP version:

srw-rw---- 1 www-data www-data 0 Jan  1 00:00 phpX.X-fpm.sock

However, if no socket file exists, PHP-FPM is not running. In that case, start it with your version-specific service name.

502 Bad Gateway Error

A 502 error typically indicates PHP-FPM is not running or the socket path is incorrect. Therefore, check PHP-FPM status using your version-specific service name:

For Debian 13:

sudo systemctl status php8.4-fpm

On Debian 12:

sudo systemctl status php8.2-fpm

With Debian 11:

sudo systemctl status php7.4-fpm

If the service shows as inactive or failed, start it:

sudo systemctl start php8.4-fpm  # Adjust version as needed

Additionally, verify that the socket path in your Nginx configuration matches the actual socket in /run/php/. A mismatch between the configured and actual socket path is a common cause of 502 errors.

MariaDB Connection Refused

When PHP cannot connect to MariaDB, start by verifying the service is running:

sudo systemctl status mariadb

Then, test the connection from the command line:

sudo mariadb -e "SELECT VERSION();"

If this command succeeds while PHP still cannot connect, verify that your application uses the correct database credentials and socket path. MariaDB on Debian uses /var/run/mysqld/mysqld.sock for MySQL compatibility, which is the path most PHP applications expect.

Remove LEMP Stack Components

If you decide to remove the LEMP stack, follow these steps for each component. This section covers removing packages installed via APT and cleaning up configuration files.

Remove Nginx

sudo systemctl stop nginx
sudo apt remove --purge nginx nginx-common -y
sudo apt autoremove -y

Optionally, you may also remove configuration files and site data:

Warning: The following command permanently deletes all Nginx configuration files and logs. Back up any custom configurations before proceeding.

sudo rm -rf /etc/nginx /var/log/nginx

Remove MariaDB

sudo systemctl stop mariadb
sudo apt remove --purge mariadb-server mariadb-client mariadb-common -y
sudo apt autoremove -y

Similarly, remove databases and configuration files:

Warning: The following command permanently deletes all databases, user data, and MariaDB configuration. Export any databases you need to keep before proceeding.

sudo rm -rf /var/lib/mysql /etc/mysql /var/log/mysql

Remove PHP

Next, remove PHP and all installed extensions. The command varies by PHP version:

For Debian 13 (PHP 8.4):

sudo apt remove --purge php8.4* -y
sudo apt autoremove -y

On Debian 12 (PHP 8.2):

sudo apt remove --purge php8.2* -y
sudo apt autoremove -y

With Debian 11 (PHP 7.4):

sudo apt remove --purge php7.4* -y
sudo apt autoremove -y

Optionally, remove PHP configuration directories:

Warning: The following command permanently deletes all PHP configuration files. Back up any custom PHP configurations before proceeding.

sudo rm -rf /etc/php

Verify Removal

Finally, confirm all packages have been removed:

dpkg -l | grep -E "nginx|mariadb|php"

If successful, the command produces no output, confirming all LEMP packages have been successfully removed. Any remaining entries indicate packages that still need removal.

Additionally, to verify services are no longer listening on web ports:

sudo ss -tlnp | grep -E ':80|:443'

Similarly, no output confirms that nothing is listening on web ports after removal.

Conclusion

At this point, your Debian server runs a complete LEMP stack with Nginx handling web requests, MariaDB managing databases, and PHP-FPM processing dynamic content. As a result, the SSL certificate encrypts all traffic, and the firewall rules protect your server from unauthorized access.

Moving forward, consider deploying a content management system with our WordPress installation guide, or set up phpMyAdmin for database management. For production environments, implement regular backups, configure log rotation, and consider adding a caching layer like Redis or Memcached to improve performance.

Leave a Comment