LEMP Stack
Table of contents
- Overview
- Install System Dependencies
- Install Nginx
- Install MySQL/MariaDB
- Install PHP 8.4
- Configure Nginx and PHP-FPM
- Install phpMyAdmin
- Install Composer
- Install Node.js & NPM
- Project Setup
- Troubleshooting
- References
1. Overview Table of Contents
This guide walks through setting up a LEMP stack (Linux, Nginx, MySQL/MariaDB, PHP) on Ubuntu (22.04 LTS), Debian (LMDE), and RHEL (Rocky Linux) for deploying PHP applications, including the Chappy.php framework.
Requirements
- Nginx
- PHP 8.3+
- MySQL or MariaDB
- Composer
- Node.js & NPM
- Git (for cloning the repository)
2. Install System Dependencies Table of Contents
First, update your system and install essential dependencies:
Ubuntu & Debian
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl wget git unzip software-properties-common net-tools
Rocky Linux (RHEL-based)
sudo dnf install -y epel-release
sudo dnf update -y
sudo dnf install -y curl wget git unzip net-tools
3. Install Nginx Table of Contents
A. Install Nginx and enable it to start on boot:
Ubuntu and Debian
sudo apt install -y nginx
sudo systemctl enable nginx
sudo systemctl start nginx
Rocky Linux (RHEL-based)
sudo dnf install -y nginx
sudo systemctl enable nginx
sudo systemctl start nginx
B. Verify Nginx is Running
To confirm that Nginx is running:
systemctl status nginx
The default page for Nginx should be accessible at:
http://localhost
4. Install MySQL/MariaDB Table of Contents
This section will guide you through installing MySQL or MariaDB, configuring it securely, and verifying the installation.
A. Install MySQL or MariaDB
Depending on your Linux distribution, install either MySQL or MariaDB using the following instructions.
Ubuntu
To install MySQL:
sudo apt install -y mysql-server
sudo systemctl enable mysql
sudo systemctl start mysql
Ubuntu and Debian
To install MariaDB:
sudo apt install -y mariadb-server
sudo systemctl enable mariadb
sudo systemctl start mariadb
Rocky Linux (RHEL-based)
To install MySQL:
sudo dnf install -y https://dev.mysql.com/get/mysql80-community-release-el8-1.noarch.rpm
sudo dnf install -y mysql-server
sudo systemctl enable mysqld
sudo systemctl start mysqld
To install MariaDB:
sudo dnf install -y mariadb-server
sudo systemctl enable mariadb
sudo systemctl start mariadb
Verify MySQL/MariaDB Installation
Run the following command to check the installed version:
mysql -V
B. Secure MySQL/MariaDB Using mysql_secure_installation
Run the mysql_secure_installation script to secure your database by setting a root password and removing default insecure settings.
Ubuntu/Debian (MySQL 8+)
sudo mysql_secure_installation
- MySQL 8+ on Ubuntu/Debian defaults to auth_socket authentication, meaning the root user does NOT need a password to log in locally.
- The root password setup step may be skipped during the process.
Rocky Linux (RHEL-based)
sudo mysql_secure_installation
- MySQL on Rocky Linux requires setting a root password during installation.
C. Steps for Running mysql_secure_installation
The script will ask a series of security questions. Here’s a breakdown of the common prompts and how to respond:
1. VALIDATE PASSWORD COMPONENT (Password Policy)
You will see this message:
VALIDATE PASSWORD COMPONENT can be used to test passwords
and improve security. It checks the strength of password
and allows the users to set only those passwords which are
secure enough. Would you like to setup VALIDATE PASSWORD component?
Press y|Y for Yes, any other key for No:
- ✅ MySQL: Type n unless you want strict password rules. Choosing y might cause errors (e.g., ERROR 1819 (HY000)) during tools like phpMyAdmin setup.
- ✅ MariaDB: This step may not appear. If it does, it’s safe to disable it (n) for local or development use.
- If you choose y, you must select a password strength level:
0 = LOW (minimum 8 characters)
1 = MEDIUM (includes numbers, mixed case, special characters)
2 = STRONG (must contain dictionary words + mixed case, numbers, and special characters)
Choose 1
for a good balance.
2. Set or Change the Root Password
Ubuntu/Debian
- If auth_socket is enabled, this step will be skipped.
- If prompted:
Would you like to set up a root password? [Y/n]
Rocky Linux (RHEL-based)
- You must set a root password.
Would you like to set up a root password? [Y/n]
- MySQL (Ubuntu/Debian): Skipped if
auth_socket
is enabled. - MySQL (RHEL-based): You must set a password.
- ✅ MariaDB (All distros): You’ll be asked even if a password is already set. Choose
n
if you already have it protected with a password or socket.
3. Switch to unix_socket Authentication (MariaDB Only)
Switch to unix_socket authentication [Y/n]
- ✅ MariaDB on Debian/Ubuntu: Choose
n
if you want to use password authentication (especially for phpMyAdmin compatibility). - Choose
y
only if you’re confident with CLI-only login and not using GUI tools like phpMyAdmin.
4. Remove Anonymous Users
- You’ll see this prompt:
Remove anonymous users? (Press y|Y for Yes, any other key for No) :
- Type
Y
and press Enter to improve security.
5. Disable Remote Root Login
- You’ll see this prompt:
Disallow root login remotely? (Press y|Y for Yes, any other key for No) :
- Type
Y
and press Enter to prevent unauthorized remote access.
6. Remove the Test Database
- You’ll see:
Remove test database and access to it? (Press y|Y for Yes, any other key for No) :
- Type
Y
and press Enter.
7. Reload Privilege Tables
- Finally, MySQL will ask:
Reload privilege tables now? (Press y|Y for Yes, any other key for No) :
- Type
Y
and press Enter to apply all changes.
8. Secure Installation Complete
- You should see a message like:
All done! If you've completed all of the above steps, your MySQL installation should now be secure.
D. Verify MySQL is Secure
After running mysql_secure_installation
, you can verify your settings by logging in:
sudo mysql -u root -p
- If prompted, enter the root password you set earlier.
Run this SQL command to authentication settings:
SELECT user, host FROM mysql.user;
- Ensure that root@localhost exists and that anonymous users were removed.
E. Changing MySQL Authentication (Ubuntu/Debian)
The step to set root password is skipped in Ubuntu and Debian.
Skipping password set for root as authentication with auth_socket is used by default.
If you would like to use password authentication instead, this can be done with the "ALTER_USER" command.
See https://dev.mysql.com/doc/refman/8.0/en/alter-user.html#alter-user-password-management for more information.
F. How to Switch MySQL Root to Password Authentication (Ubuntu & Debian)
If you want to use a password for the root user instead of auth_socket, follow these steps:
1. Log into MySQL as Root
Since auth_socket
is enabled, use sudo to access MySQL without a password:
sudo mysql
2. Check Current Authentication Method
Run this SQL command:
SELECT user, host, plugin FROM mysql.user;
You should see root@localhost
using auth_socket, like this:
+------+-----------+-------------+
| user | host | plugin |
+------+-----------+-------------+
| root | localhost | auth_socket |
+------+-----------+-------------+
3. Change Root to Use Password Authentication
To switch from auth_socket
to mysql_native_password
, run:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your-secure-password';
FLUSH PRIVILEGES;
4. Verify the Change
Run the authentication check again:
SELECT user, host, plugin FROM mysql.user;
It should now show mysql_native_password
instead of auth_socket
.
5. Exit and Test
Exit MySQL:
EXIT;
Now try logging in with your new password:
mysql -u root -p
6. Final Notes
- For production servers, always use a strong root password and disable remote root login.
- If you forget your MySQL root password, you’ll need to reset it manually via mysqld_safe mode.
5. Install PHP 8.4 Table of Contents
A. Ubuntu
sudo add-apt-repository ppa:ondrej/php -y
sudo apt update && sudo apt upgrade -y
sudo apt install -y php8.4 php8.4-cli php8.4-fpm php8.4-mbstring php8.4-xml php8.4-curl php8.4-zip php8.4-mysql php8.4-sqlite3 sqlite3 php8.4-bcmath
Enable and start PHP-FPM:
sudo systemctl enable php8.4-fpm
sudo systemctl start php8.4-fpm
B. Debian
1. Add the SURY repository (trusted PHP repo for Debian)
sudo apt install -y lsb-release apt-transport-https ca-certificates wget gnupg2
Then import the GPG key:
wget -qO - https://packages.sury.org/php/apt.gpg | sudo tee /etc/apt/trusted.gpg.d/php.gpg >/dev/null
Now add the repo to your sources list:
echo "deb https://packages.sury.org/php/ bookworm main" | sudo tee /etc/apt/sources.list.d/php.list
2. Update and install PHP 8.4
sudo apt update && sudo apt upgrade -y
sudo apt install -y php8.4 php8.4-cli php8.4-fpm php8.4-mysql php8.4-curl php8.4-zip php8.4-mbstring php8.4-xml php8.4-bcmath php8.4-soap php8.4-intl php8.4-readline php8.4-sqlite3 sqlite3
Enable and start PHP-FPM:
sudo systemctl enable php8.4-fpm
sudo systemctl start php8.4-fpm
C. Rocky Linux (RHEL-based)
sudo dnf install -y https://rpms.remirepo.net/enterprise/remi-release-9.rpm
sudo dnf module list php # Optional: list available versions
sudo dnf module enable php:remi-8.4 -y # Enable PHP 8.4 from Remi
sudo dnf install -y php php-cli php-fpm php-mbstring php-xml php-curl php-zip php-mysqlnd php-bcmath php-json php-gd php-opcache php-intl php-pear php-soap
Enable and start PHP-FPM:
sudo systemctl enable php-fpm
sudo systemctl start php-fpm
D. Verify installation:
php -v
6. Configure Nginx and PHP-FPM Table of Contents
A. Configure Nginx Server Block
Clone and move the project:
cd ~/
git clone git@github.com:chapmancbVCU/chappy-php.git
sudo mv chappy-php/ /var/www/
cd /var/www/chappy-php
B. Set proper permissions:
Ubuntu
sudo chown -R your-username:www-data /var/www/chappy-php
sudo chmod -R 755 /var/www/chappy-php
Rocky Linux (RHEL-based)
sudo chown -R your-username:nginx /var/www/chappy-php
sudo chmod -R 755 /var/www/chappy-php
C. Create a new configuration file:
Ubuntu & Debian
sudo vi /etc/nginx/sites-available/chappy-php
Paste the following content while making sure correct php version is set (replace server_domain_or_IP with your IP address or domain name):
server {
listen 80;
server_name server_domain_or_IP;
root /var/www/chappy-php;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
index index.html index.htm index.php;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 404 /index.php;
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/var/run/php/php8.4-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
Enable the new site and disable default on Ubuntu/Debian:
sudo ln -s /etc/nginx/sites-available/chappy-php /etc/nginx/sites-enabled/
sudo unlink /etc/nginx/sites-enabled/default
Rocky Linux (RHEL-based)
sudo vi /etc/nginx/conf.d/chappy-php.conf
Paste the following content while making sure correct php version is set (replace server_domain_or_IP with your IP address or domain name):
server {
listen 80;
server_name server_domain_or_IP;
root /var/www/chappy-php;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
index index.html index.htm index.php;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 404 /index.php;
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
Allow Nginx to Write and Connect:
sudo setsebool -P httpd_unified 1
sudo setsebool -P httpd_read_user_content 1
sudo setsebool -P httpd_can_network_connect 1
sudo setsebool -P httpd_enable_homedirs 1
D. Test PHP
Then test the configuration and reload Nginx:
sudo nginx -t
sudo systemctl reload nginx
Create a test file:
echo "<?php phpinfo(); ?>" | sudo tee /var/www/chappy-php/info.php
Open in a browser:
http://localhost/info.php
Remove the file after testing:
sudo rm /var/www/chappy-php/info.php
E. Configure Upload Size (for Profile Image Support):
Ubuntu and Debian
sudo vi /etc/php/8.4/fpm/php.ini
Rocky Linux (RHEL-based)
sudo vi /etc/php.ini
Then modify the setting
upload_max_filesize = 2M
to a value appropriate for your needs. We set it to 10M
.
Then restart PHP-FPM:
Ubuntu and Debian
sudo systemctl restart php8.4-fpm
Rocky Linux (RHEL-based)
sudo systemctl restart php-fpm
7. Install phpMyAdmin Table of Contents
phpMyAdmin provides a web interface to manage MySQL or MariaDB databases.
A. Install phpMyAdmin
Ubuntu & Debian
sudo apt install -y phpmyadmin
When prompted:
- Do not select a web server (Nginx is not listed).
- Choose Yes to configure dbconfig-common and create the phpMyAdmin database.
- Set a phpMyAdmin password or leave it blank to auto-generate.
Rocky Linux (RHEL-based)
sudo dnf install -y phpmyadmin
B. Configure Nginx to Serve phpMyAdmin
Ubuntu & Debian Symlink phpMyAdmin into your Nginx-accessible root path:
sudo ln -s /usr/share/phpmyadmin /var/www/phpmyadmin
Then edit your Nginx config:
sudo vi /etc/nginx/sites-enabled/chappy-php
Add the following inside the server {}
block:
location /phpmyadmin {
root /var/www;
index index.php index.html index.htm;
location ~ ^/phpmyadmin/(.+\.php)$ {
try_files $uri =404;
root /var/www;
fastcgi_pass unix:/var/run/php/php8.4-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~* ^/phpmyadmin/(.+\.(jpg|jpeg|gif|css|png|js|ico|html|xml|txt))$ {
root /var/www;
}
}
- Note: If you get a 202 Bad Gateway error check the version of PHP in your file. If you’re using a custom root path like /var/www/chappy-php, adjust root accordingly.
Rocky Linux (RHEL-based) Symlink phpMyAdmin into your Nginx-accessible root path:
sudo ln -s /usr/share/phpMyAdmin /var/www/phpmyadmin
Then edit your Nginx config:
sudo vi /etc/nginx/conf.d/chappy-php.conf
Add the following inside the server {}
block:
location /phpmyadmin {
root /var/www;
index index.php index.html index.htm;
location ~ ^/phpmyadmin/(.+\.php)$ {
try_files $uri =404;
root /var/www;
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~* ^/phpmyadmin/(.+\.(jpg|jpeg|gif|css|png|js|ico|html|xml|txt))$ {
root /var/www;
}
}
Reload Nginx to apply the config:
sudo nginx -t
sudo systemctl reload nginx
C. Verify phpMyAdmin Installation
Open your browser and visit:
http://localhost/phpmyadmin
Log in using:
- Username: root
- Password: (set during MySQL/MariaDB setup)
⚠️ If you’re using
auth_socket
, see Section 4F to switch to mysql_native_password.
D. Setup Your Database
- In the left panel click on the New link.
- In the main panel under Create Database enter the name for your database. This will be the database you will set to
DB_DATABASE
in your.env
file. - Click create.
8. Install Composer Table of Contents
Composer is required to manage PHP dependencies.
A. Download and Install Composer
cd ~/
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
B. Verify Installation
composer -v
9. Install Node.js & NPM Table of Contents
Use NodeSource to install the latest stable Node.js version.
A. Add Node.js Repository
Ubuntu and Debian
sudo apt install -y ca-certificates # On minimal OS installs
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
Rocky Linux (RHEL-based)
# ca-certificates on minimal OS installs
rpm -q ca-certificates || sudo dnf install -y ca-certificates
curl -fsSL https://rpm.nodesource.com/setup_lts.x | sudo bash -
B. Install Node.js & NPM
Ubuntu
sudo apt install -y nodejs
Rocky Linux (RHEL-based)
sudo dnf install -y nodejs
C. Verify Installation
node -v
npm -v
10. Project Setup Table of Contents
A. Navigate to your user’s root directory and install dependencies:
cd /var/www/chappy-php/
composer run install-project
B. Project Configuration
Open your preferred IDE (We use VSCode) and edit the .env
file:
- Set
APP_DOMAIN
TO/
. - Update the database section:
# Set to mysql or mariadb for production DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 # Set to your database name for production DB_DATABASE=your_db_name DB_USER=root DB_PASSWORD=your_password
Use a user other than root
on a production environment
C. Update /etc/hosts (For Custom Domain)
If you want to access your site using http://chappyphp.local, you can edit your /etc/hosts file:
sudo vi /etc/hosts
Example configuration:
127.0.0.1 localhost chappyphp.local
127.0.1.1 ubuntu-vm
your_ip_addr chappyphp.local
Now, http://chappyphp.local will work as expected.
D. Enable the Site:
Restart Nginx After Modifying Server Block or /etc/hosts:
sudo systemctl restart nginx
See Section D in Troubleshooting if you are having issues with Rocky Linux (RHEL)
Rocky Linux (RHEL-based)
1. Fix SELinux Contexts for Nginx
Allow Nginx to read and serve content from your project directory:
sudo chcon -Rt httpd_sys_content_t /var/www/chappy-php
Also apply recursively to any .htaccess, uploads, or views:
sudo restorecon -Rv /var/www/chappy-php
2. Set the Correct SELinux Context for Writable Log Files and to Storage:
Step 1: Apply the Right Context:
sudo chcon -R -t httpd_sys_rw_content_t /var/www/chappy-php/storage
Step 2: Make it Persistent (Survives Reboots):
sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/chappy-php/storage(/.*)?"
sudo restorecon -Rv /var/www/chappy-php/storage
📁 If you’re using other writable paths, repeat these steps for those as well.
3. Add Firewalld Rules for Nginx
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
Now restart Nginx:
sudo systemctl restart nginx
F. Final Steps
Set permissions and ownership for storage directory (This will enable writing to logs and uploads): Ubuntu & Debian
sudo chown -R chadchapman:www-data storage/
sudo chmod -R 775 storage/
Rocky Linux (RHEL-based)
sudo chown -R nginx:nginx storage/
sudo chmod -R 777 storage/
Run migrations:
php console migrate
Your project should now be accessible at:
http://<your_ip_address>
11. Troubleshooting Table of Contents
A. Common Issues:
- ERROR 1819 (HY000) during phpMyAdmin installation → Your password does not meet MySQL’s policy. Disable VALIDATE PASSWORD or use a strong password.
- mysql_secure_installation skips root password setup on Ubuntu/Debian → Run:
sudo mysql ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your-password'; FLUSH PRIVILEGES;
B. SELinux Permissions for phpMyAdmin (Rocky Linux)
Since phpMyAdmin
is installed via dnf
on Rocky Linux, SELinux might block it from reading /var/www/phpmyadmin
.
🔹 Fix: If users get 403 Forbidden on phpMyAdmin, they need to run:
sudo chcon -R -t httpd_sys_content_t /usr/share/phpmyadmin
sudo semanage fcontext -a -t httpd_sys_content_t "/usr/share/phpmyadmin(/.*)?"
sudo restorecon -Rv /usr/share/phpmyadmin
C. MySQL 8 Authentication in Rocky Linux
If mysql_secure_installation
doesn’t ask for a password, users may need to set one manually:
sudo mysql
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your-secure-password';
FLUSH PRIVILEGES;
EXIT;
12. References Table of Contents
A. How To Install Linux, Nginx, MySQL, PHP (LEMP stack) on Ubuntu
B. How To Install and Configure Laravel with Nginx on Ubuntu 22.04 (LEMP)
C. How to Install PHP 8.3 on Ubuntu 22.04
D. How To Install and Secure phpMyAdmin with Nginx on an Ubuntu 20.04 Server