Install Nginx, MySQL, and PHP on an ECS instance with full control over configuration and security hardening for production websites.
Solution architecture
|
Procedure
We recommend an ECS instance with at least 2 GiB of memory.
Step 1: Prepare the instance
Configure public access and security group rules, then update system packages.
Configure public access.
Ensure your ECS instance has a public IP address or an Elastic IP Address (EIP). See Enable public access for an instance.
Add a security group rule.
In the security group of your ECS instance, add an inbound rule to allow TCP traffic on port 80. See Add a security group rule.
Update system packages.
Alibaba Cloud Linux 3 / CentOS 8
Log on to the ECS instance.
Go to ECS console - Instances. In the top-left corner, select the region and resource group for the target resource.
On the instance details page, click Connect and select Workbench. Follow the on-screen prompts to log on.
Update all packages.
sudo dnf update -y
Alibaba Cloud Linux 2 / CentOS 7
Log on to the ECS instance.
Go to ECS console - Instances. In the top-left corner, select the region and resource group for the target resource.
On the instance details page, click Connect and select Workbench. Follow the on-screen prompts to log on.
Update all packages.
sudo yum update -y
Ubuntu
Log on to the ECS instance.
Go to ECS console - Instances. In the top-left corner, select the region and resource group for the target resource.
On the instance details page, click Connect and select Workbench. Follow the on-screen prompts to log on.
Update all packages.
sudo apt update -y && sudo apt upgrade -y
Step 2: Install Nginx
Add the 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 nginxAlibaba 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 nginxUbuntu
# Install dependencies for Nginx. sudo apt install -y curl gnupg2 ca-certificates lsb-release ubuntu-keyring # Import the official Nginx signing key to verify package authenticity. 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 nginxStart Nginx and enable it on boot.
The
enable --nowflag starts the service and enables it on boot.sudo systemctl enable --now nginxVerify that Nginx is running.
Runcurl http://127.0.0.1. If the output contains the Nginx welcome page HTML, the installation succeeded.
Step 3: Install MySQL
Install 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-serverAlibaba 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-serverUbuntu
# 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-serverStart MySQL and enable it on boot.
Alibaba Cloud Linux / CentOS
sudo systemctl enable --now mysqldUbuntu
sudo systemctl enable --now mysqlPerform security hardening.
The default MySQL configuration allows anonymous users and remote root logon. Run the security script to fix these vulnerabilities.
Get the initial root password.
This step applies only to Alibaba Cloud Linux and CentOS. Ubuntu uses socket-based authentication for root by default.
# Extract and display the temporary password from the log. sudo grep 'temporary password' /var/log/mysqld.logRun the security script.
Run
sudo mysql_secure_installationand follow the prompts:Reset the root password: Use the temporary password to log on and set a new password.
Use a strong password of at least 12 characters with uppercase, lowercase, numbers, and special characters.
Remove anonymous users: Enter Y.
Disallow remote root logon: Enter Y.
Remove the test database: Enter Y.
Reload the privilege tables: Enter Y.
Step 4: Install PHP
Install PHP, PHP-FPM, and the MySQL extension.
Add a PHP repository and install PHP.
Alibaba Cloud Linux 3 / CentOS 8
# Add the Remi 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 yum-utils. dnf install -y yum-utils # Install PHP, PHP-FPM, and the MySQL extension. dnf install -y php php-fpm php-mysqlndAlibaba 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 the PHP 8.2 repository. 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-mysqlndUbuntu
# Install the tool for managing PPAs. 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-mysqlStart PHP-FPM and enable it on boot.
Alibaba Cloud Linux / CentOS
sudo systemctl enable --now php-fpmUbuntu
sudo systemctl enable --now php8.2-fpm
Step 5: Configure Nginx to process PHP
By default, Nginx serves only static files. Configure it to forward .php requests to PHP-FPM. Without this, PHP pages will be downloaded or trigger errors.
Back up the default Nginx configuration file.
sudo cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bakGet the communication address for the PHP-FPM service.
Nginx uses this address to forward PHP requests to PHP-FPM.
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"Configure Nginx to forward
.phprequests to PHP-FPM.Replace
<PHP_FPM_ADDRESS>with the communication address from the previous step and run the command.If the output is a file path (for example, /run/php-fpm/www.sock), the service uses a Unix socket. The address format is
unix:FILE_PATH, such asunix:/run/php-fpm/www.sock.If the output is an IP address and port (for example, 127.0.0.1:9000), the service uses a TCP socket. Use the IP address and port 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 files. 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_FPM_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; } } EOFTest the configuration file.
sudo nginx -tIf the output contains successful, the configuration is correct. Proceed to the next step.
If the output contains failed, verify that you replaced the address correctly, or restore the backup:
sudo mv /etc/nginx/conf.d/default.conf.bak /etc/nginx/conf.d/default.conf
Restart Nginx to apply the new configuration.
sudo systemctl restart nginx
Step 6: Verify the stack
Create test files to verify that Nginx processes PHP and PHP connects to MySQL.
Verify Nginx processing of PHP.
Create a test file: Generate a file with
phpinfo()to display the PHP configuration.echo "<?php phpinfo(); ?>" | sudo tee /usr/share/nginx/html/phpinfo.phpVerify in a browser: Navigate to
http://<ECS_PUBLIC_IP>/phpinfo.php.Success: The page displays PHP environment details.
Failure: The page displays "File not found" or the browser downloads the file.
Clean up the test file: Delete this file after verification. The
phpinfopage exposes sensitive server information.
Verify the PHP-to-MySQL connectivity.
Create a PHP script to test database connectivity with the mysqlnd extension.
Create a database and a user.
Log in to MySQL with therootpassword set in Step 3.sudo mysql -u root -pAt the
mysql>prompt, create a database namedwebapp, a user namedwebuser, and grant the user 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;Create a database connection test file.
Replace<YourPassword>with the user password from the previous step and run the command.sudo tee /usr/share/nginx/html/test.php <<-'EOF' <?php $servername = "localhost"; $username = "webuser"; $password = "<YourPassword>"; $dbname = "webapp"; // Create a connection $conn = new mysqli($servername, $username, $password, $dbname); // Check the connection if ($conn->connect_error) { die("Database connection failed: " . $conn->connect_error); } echo "Database connection successful!"; ?> EOFVerify in a browser: Navigate to
http://<ECS_PUBLIC_IP>/test.php. If the page displays Database connection successful!, the connection works.Clean up the test file: This script contains database credentials in plain text. Delete it immediately after testing.
FAQ
Browser timeout or "site can't be reached"
This error usually indicates a network issue. Troubleshoot in the following order:
Security group: Verify that the security group allows inbound traffic on port 80.
Firewall: Verify that the OS firewall (such as
firewalldorufw) allows traffic on port 80.Nginx service: Run
sudo systemctl status nginxto check if Nginx is running. If not, check the logs withsudo journalctl -xeu nginx.Port conflict: Check whether port 80 is occupied by another application. See Troubleshoot port connectivity issues for an ECS instance.
Browser displays "502 Bad Gateway"
This error means Nginx cannot communicate with PHP-FPM.
PHP-FPM service: Run
sudo systemctl status php-fpm(orphp8.2-fpmon Ubuntu) to check if the service is running.Socket path: Verify that the
unix:path in thefastcgi_passdirective matches thelistenpath in the PHP-FPM configuration.SELinux/AppArmor: On CentOS or Alibaba Cloud Linux, SELinux may block Nginx-to-PHP-FPM communication. Run
sestatusand check/var/log/audit/audit.logfor denials.Socket permissions: Verify that the Nginx user has read/write access to the socket file.
Enable remote access to MySQL
By default, MySQL prohibits remote logon. To enable it, create a dedicated remote-access user instead of using root. See Add a user for remote MySQL access.