All Products
Search
Document Center

Elastic Compute Service:Deploy an LNMP stack

Last Updated:Dec 09, 2025

This topic walks you through building a LNMP (Linux, Nginx, MySQL, PHP) stack on an Elastic Compute Service (ECS) instance using a package manager. It supports multiple operating systems, including Alibaba Cloud Linux, CentOS, and Ubuntu. By deploying manually, you can overcome the limitations of pre-built images, which often complicate the customization of configuration files. This approach gives you full control over the underlying environment and enables security hardening to meet the production requirements for dynamic websites like WordPress and Magento.

Solution architecture

image
  1. A user's browser sends an HTTP request to Port 80 of the ECS instance over the public network.

  2. As the web server, Nginx receives and routes the request. If it's a request for a static resource (like HTML, CSS, or images), Nginx reads and returns it directly. If it's a request for a PHP script (like a .php file), Nginx forwards it to PHP-FPM for processing using the FastCGI protocol.

  3. PHP-FPM (FastCGI Process Manager) receives and executes the PHP code. If the code needs to interact with a database, it connects to the MySQL database.

  4. MySQL stores and manages data, executes SQL queries sent from the PHP script, and returns the results to PHP-FPM.

  5. PHP-FPM generates HTML content from the result and returns it to Nginx, which then sends the final response to the user's browser.

Procedure

For a smooth experience, we recommend an ECS instance with at least 2 GiB of memory.

Step 1: Prepare the instance

Configure a public IP address and security group to ensure the instance is accessible from the internet, and update system components to patch potential security vulnerabilities.

  1. Configure public network access.

    Ensure your ECS instance has a public IP address assigned or an Elastic IP Address (EIP) bound to it. For detailed instructions, see Configure public bandwidth.

  2. Add a security group rule.

    In the security group associated with your ECS instance, add an inbound rule to allow traffic on TCP Port 80 (for HTTP access). For detailed instructions, see Add a security group rule.

  3. Update the system and software packages.

    Alibaba Cloud Linux 3 / CentOS 8

    1. Log on to the ECS instance.

      1. Go to ECS console - Instances. In the top navigation bar, select the target region and resource group.

      2. Go to the instance details page. Click Connect and select Workbench. Follow the on-screen prompts to log on and open the terminal.

    2. Update the package index and upgrade all packages to their latest versions.

      sudo dnf update -y

    Alibaba Cloud Linux 2 / CentOS 7

    1. Log on to the ECS instance.

      1. Go to ECS console - Instances. In the top navigation bar, select the target region and resource group.

      2. On the instance details page, click Connect and select Workbench. Follow the on-screen prompts to log on and open the terminal.

    2. Update all packages to their latest versions.

      sudo yum update -y

    Ubuntu

    1. Log on to the ECS instance.

      1. Go to ECS console - Instances. In the top navigation bar, select the target region and resource group.

      2. On the instance details page, click Connect and select Workbench. Follow the on-screen prompts to log on and open the terminal.

    2. Update all packages to their latest versions.

      sudo apt update -y && sudo apt upgrade -y

Step 2: Install and configure Nginx

  1. Add the official Nginx repository and install Nginx.

    Alibaba Cloud Linux 3 / CentOS 8

    # Add the official Nginx repository.
    sudo tee /etc/yum.repos.d/nginx.repo <<-'EOF'
    [nginx-stable]
    name=nginx stable repo
    baseurl=https://nginx.org/packages/centos/8/$basearch/
    gpgcheck=1
    enabled=1
    gpgkey=https://nginx.org/keys/nginx_signing.key
    module_hotfixes=true
    EOF
    
    # Install Nginx.
    sudo dnf -y install nginx

    Alibaba Cloud Linux 2 / CentOS 7

    # Add the official Nginx repository.
    sudo tee /etc/yum.repos.d/nginx.repo <<-'EOF'
    [nginx-stable]
    name=nginx stable repo
    baseurl=https://nginx.org/packages/centos/7/$basearch/
    gpgcheck=1
    enabled=1
    gpgkey=https://nginx.org/keys/nginx_signing.key
    module_hotfixes=true
    EOF
    
    # Install Nginx.
    sudo yum -y install nginx

    Ubuntu

    # Install Nginx dependencies.
    sudo apt install -y curl gnupg2 ca-certificates lsb-release ubuntu-keyring
    
    # Import the official Nginx signature key to verify the package source.
    curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
    
    # Set up the APT repository for Nginx.
    echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" | sudo tee /etc/apt/sources.list.d/nginx.list
    
    # Install Nginx.
    sudo apt update
    sudo apt install -y nginx
  2. Start Nginx and enable it to start on boot.

    The enable --now flag both starts the service immediately and enables it to start on boot.

    sudo systemctl enable --now nginx
  3. Verify the Nginx service.
    Run curl http://127.0.0.1. If the terminal outputs the HTML code for the Nginx welcome page, the installation was successful.

Step 3: Install and harden MySQL

  1. Install and start MySQL.

    Alibaba Cloud Linux 3 / CentOS 8

    # Alibaba Cloud Linux 3 requires compat-openssl10 for compatibility with older OpenSSL versions.
    if [ -f /etc/os-release ]; then
        . /etc/os-release
        if [ "$ID" = "alinux" ] && [ "$VERSION_ID" = "3" ]; then
            sudo yum install -y compat-openssl10
        fi
    fi
    
    # Add the YUM repository for MySQL 8.4.
    sudo rpm -Uvh https://repo.mysql.com/mysql84-community-release-el8-1.noarch.rpm
    
    # Install the MySQL service.
    sudo dnf install -y mysql-server

    Alibaba Cloud Linux 2 / CentOS 7

    # Add the repository for MySQL 8.4.
    sudo rpm -Uvh https://repo.mysql.com/mysql84-community-release-el7-1.noarch.rpm
    
    # Install the MySQL service.
    sudo yum install -y mysql-server

    Ubuntu

    # The default Ubuntu repository includes MySQL, so you can install it directly.
    # Ubuntu 20.04 and later versions install MySQL 8.0 by default.
    # Ubuntu 18.04 and earlier versions install MySQL 5.x by default.
    sudo apt install -y mysql-server
  2. Start MySQL and enable it to start on boot.

    Alibaba Cloud Linux / CentOS

    sudo systemctl enable --now mysqld

    Ubuntu

    sudo systemctl enable --now mysql
  3. Perform security hardening.

    MySQL's default configuration is insecure, allowing anonymous users and remote root logon. Run the included security script to address these vulnerabilities.

    1. Get the initial root password.

      This step only applies to Alibaba Cloud Linux/CentOS, as Ubuntu uses passwordless authentication by default. Use this temporary password in the next step to reset the root password.

      # Extract and display the temporary password from the log.
      sudo grep 'temporary password' /var/log/mysqld.log
    2. Run the hardening script.

      Run the command sudo mysql_secure_installation and follow the prompts to complete the recommended configurations:

      1. Reset the root password: Use the temporary password from the previous step to log on and set a new password.

        We recommend setting a strong password that is at least 12 characters long and includes uppercase and lowercase letters, numbers, and special symbols.
      2. Remove anonymous users: Enter Y.

      3. Disallow remote root logon: Enter Y.

      4. Remove the test database: Enter Y.

      5. Reload the privilege tables: Enter Y.

Step 4: Install and configure PHP

Install PHP, PHP-FPM (for handling web requests), and the MySQL driver.

  1. Add the PHP repository and install PHP.

    Alibaba Cloud Linux 3 / CentOS 8

    # Add the PHP repository.
    cat > /etc/yum.repos.d/remi.repo << 'EOF'
    [remi]
    name=Remi\'s RPM Repository for Enterprise Linux 8 - x86_64
    baseurl=https://rpms.remirepo.net/enterprise/8/remi/x86_64/
    enabled=1
    gpgcheck=1
    gpgkey=https://rpms.remirepo.net/RPM-GPG-KEY-remi2024
    
    [remi-safe]
    name=Remi Safe Repository
    baseurl=https://rpms.remirepo.net/enterprise/8/safe/x86_64/
    enabled=1
    gpgcheck=1
    gpgkey=https://rpms.remirepo.net/RPM-GPG-KEY-remi2024
    EOF
    
    # Import the GPG Key.
    rpm --import https://rpms.remirepo.net/RPM-GPG-KEY-remi2024
    
    # Install tools and enable the module.
    dnf install -y yum-utils
    
    # Install PHP.
    dnf install -y php php-fpm php-mysqlnd

    Alibaba Cloud Linux 2 / CentOS 7

    # Install the Remi repository.
    sudo yum install -y http://rpms.remirepo.net/enterprise/remi-release-7.rpm
    
    # Install yum-utils and enable PHP 8.2.
    sudo yum install -y yum-utils
    sudo yum-config-manager --enable remi-php82
    
    # Install PHP, PHP-FPM, and the MySQL extension.
    sudo yum install -y php php-fpm php-mysqlnd

    Ubuntu

    # Install the PPA management tool.
    sudo apt install -y software-properties-common
    
    # Add the ondrej/php PPA repository.
    sudo add-apt-repository -y ppa:ondrej/php
    
    # Install PHP 8.2, PHP-FPM, and the MySQL extension.
    sudo apt install -y php8.2 php8.2-fpm php8.2-mysql
  2. Start PHP-FPM and enable it to start on boot.

    Alibaba Cloud Linux / CentOS

    sudo systemctl enable --now php-fpm

    Ubuntu

    sudo systemctl enable --now php8.2-fpm

Step 5: Configure Nginx to support PHP processing

By default, Nginx can only serve static web pages (like HTML and images). To support dynamic websites like WordPress, you must configure Nginx to forward .php file requests to PHP-FPM for processing. Otherwise, when you try to access a PHP page, the file may be downloaded directly or fail to display.

  1. Back up the Nginx configuration file.

    sudo cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak
  2. Get the communication address for the PHP service.

    Nginx uses this address to forward PHP requests to PHP-FPM for processing.

    Alibaba Cloud Linux / CentOS

    PHP_FPM_LISTEN=$(sudo sed -n 's/^\s*listen\s*=\s*//p' /etc/php-fpm.d/www.conf | head -n 1)
    echo "$PHP_FPM_LISTEN"

    Ubuntu

    PHP_FPM_LISTEN=$(sudo sed -n 's/^\s*listen\s*=\s*//p' /etc/php/8.2/fpm/pool.d/www.conf | head -n 1)
    echo "$PHP_FPM_LISTEN"
  3. Configure Nginx to forward .php requests to the PHP service.

    Based on the output from the previous step, replace <PHP_COMMUNICATION_ADDRESS> in the command below with the actual communication address, and then run the command:

    • If the output is a file path (for example, /run/php-fpm/www.sock): The communication method is a Unix socket (for local communication only, offering better performance and security). The address format is unix:FILE_PATH, such as unix:/run/php-fpm/www.sock.

    • If the output is an IP address and port (for example, 127.0.0.1:9000): The communication method is TCP (supports cross-host communication, suitable for scenarios where PHP-FPM and Nginx are deployed on separate hosts). You can use this directly as the communication address.

    sudo tee /etc/nginx/conf.d/default.conf <<-'EOF'
    server {
        listen       80;
        server_name  localhost;
        root /usr/share/nginx/html;
    
        # Set the default index file.
        index index.php index.html index.htm;
    
        location / {
            try_files $uri $uri/ /index.php?$query_string;
        }
    
        # Forward .php requests to PHP-FPM.
        location ~ \.php$ {
            fastcgi_pass   <PHP-communication-address>;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include        fastcgi_params;
        }
    
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
    }
    EOF
  4. Check the configuration file.

    sudo nginx -t
    • If the output contains the keyword successful, the configuration is correct. You can proceed to the next step.

    • If the output contains failed, the configuration is incorrect. Check if you replaced the path in the command correctly, or run the following command to restore the configuration from the backup:

      sudo mv /etc/nginx/conf.d/default.conf.bak /etc/nginx/conf.d/default.conf
  5. Restart Nginx to apply the new configuration.

    sudo systemctl restart nginx

Step 6: Verify the LNMP stack

Create test files to verify that Nginx can process PHP and that PHP can connect to the MySQL database. This verifies that all components of the LNMP stack are working together correctly.

  1. Verify Nginx processing.

    1. Create a test file: Generate a file containing the phpinfo() function to display the detailed configuration of the PHP environment.

      echo "<?php phpinfo(); ?>" | sudo tee /usr/share/nginx/html/phpinfo.php
    2. Verify in a browser: Visit http://<ECS-public-IP-address>/phpinfo.php.

      • Success: The page renders correctly and displays detailed information about the PHP environment.

      • Failure: The page displays File not found or the file is downloaded directly.

    3. Clean up the test file: After successful verification, you must delete this file. The phpinfo page contains sensitive information that poses a security risk if exposed to the public.

  2. Verify the connection between PHP and MySQL.

    Create a PHP script to test if PHP can connect to the database using the mysqlnd extension.

    1. Create a database and user.
      Log on to MySQL with the root account (enter the root password you set in Step 3.

      sudo mysql -u root -p

      At the mysql> prompt, run the following commands to create a database named webapp and a user named webuser, and grant it all privileges on that database.

      CREATE DATABASE webapp;
      /* Replace <YourPassword> with a strong password. */
      CREATE USER 'webuser'@'localhost' IDENTIFIED BY '<YourPassword>';
      GRANT ALL PRIVILEGES ON webapp.* TO 'webuser'@'localhost';
      FLUSH PRIVILEGES;
      EXIT;
    2. Create a database connection test file.
      Replace <YourPassword> in the command below with the user password you set in the previous step, and then run the command.

      sudo tee /usr/share/nginx/html/test.php <<-'EOF'
      <?php
      $servername = "localhost";
      $username = "webuser";
      $password = "<YourPassword>";
      $dbname = "webapp";
      // Create connection
      $conn = new mysqli($servername, $username, $password, $dbname);
      // Check connection
      if ($conn->connect_error) {
          die("Database connection failed: " . $conn->connect_error);
      }
      echo "Database connection successful!";
      ?>
      EOF
      • Verify in a browser: Visit http://<ECS-public-IP-address>/test.php. If the page displays Database Connection Successful!, the test is successful.

      • Clean up the test file: This script contains the database username and password in plain text. You must delete it immediately after testing to prevent credential leakage.

FAQ

The browser times out or displays "This site can't be reached"

This is usually a network connectivity issue. Troubleshoot in the following order:

  1. Security Group: Check if the inbound rules of the instance's security group allow traffic on Port 80.

  2. Firewall: Check if the operating system's internal firewall (such as firewalld or ufw) is disabled or has a rule allowing traffic on Port 80.

  3. Nginx service: Run sudo systemctl status nginx to check if the Nginx service is running. If not, check the logs with sudo journalctl -xeu nginx to diagnose the issue.

  4. Port conflict: Check if Port 80 is being used by another program. For detailed troubleshooting steps, see Troubleshoot port connectivity issues for an ECS instance.

The browser displays "502 Bad Gateway"

This error indicates that Nginx cannot communicate properly with PHP-FPM.

  1. PHP-FPM service: Run sudo systemctl status php-fpm (or php8.2-fpm for Ubuntu) to check if the service is running.

  2. Socket path: Verify that the path following unix: in the fastcgi_pass directive in your Nginx configuration file exactly matches the listen path in your PHP-FPM configuration file.

  3. SELinux/AppArmor: On CentOS/Alibaba Cloud Linux, SELinux policies might be blocking communication between Nginx and PHP-FPM. Run sestatus to check its status and review the denial logs in /var/log/audit/audit.log.

  4. Socket permissions: Check the permissions of the socket file to ensure the user running Nginx has read and write access.

How do I allow remote access to MySQL?

By default, MySQL prohibits remote logon. If you need to enable it, we recommend creating a dedicated user for remote access instead of allowing the root user to connect remotely. For detailed instructions, see Add a user for remote MySQL access.