[ see also: Install guide for installing LAMP stack ]
[ jump to the WordPress Installation instructions ]
Prepare Your System
Welcome to your complete step-by-step guide to install NGINX, PHP, and MySQL on Linux (aka. the LEMP stack). This tutorial assumes you’re going to use a Debian based Linux system, whether it’s a Windows Subsystem for Linux (WSL) installation, or an actual Linux Distribution running outside of Windows. Common Linux distros based on Debian (at least which use the apt package manager for .deb packages) are Ubuntu, Mint, MX Linux, Kali, and many many others. Technically, you can’t install NGINX on Windows, but with WSL you can!
I like the terminal-based text editor, Micro. so I’ve included it in the preliminary installation steps. I’ve also included cURL (a command line utility for accessing information from a variety of URL’s / protocols. CURL tutorial here: find it with [CTRL+F > "cURL"]
). Most likely, cURL is already installed on your system. As if you elected me to sign a Bill in Congress, I threw in net-tools as well because I like the ifconfig command. It’s no longer installed by default, and I often forget. Now I won’t!
More on Micro
Perhaps you won’t prefer Micro over Nano as a terminal based text editor. Nevertheless, installing Micro is just a suggestion. There are a lot of options! Be your own judge about the terminal based text editors. To show numbered lines in Nano like Micro has bey default, enter the key combination [ Alt+N ]
.
To be fair, most Linux users use VIM. I don’t like VIM for the offset keyboard home keys. Decisions.
One major difference between Nano and Micro which you’ll notice straight away is that the “exit” keys are different. Nano usus [ Ctrl+X ]
to “exit”, whereas micro is [ Ctrl+Q ]
to “quit”. Also, to “save as” in micro, you’ll need to first [ Ctrl+E ]
(as in “E” for “execute”), and type: save “new file name”.
Note: under WSL (Windows Subsystem for Linux), text selections behave differently such that content you expected to copy to the clipboard might not be copied (e.g. on my system at least, micro isn’t as friendly with copy/ paste operations to/ from Windows apps to/ from WSL apps. I use Nano if I want to copy/ paste stuff). In Micro, start with [ Ctrl+E ]
and type “help commands” (sans quotes). When finished browsing Micro “help”, type [ Ctrl+E ]
again for the command line, and type “quit” to focus the cursor back in the text editor area.
As always, before making changes to your system’s software, start by updating your package lists:
sudo apt update sudo apt upgrade -f sudo apt install micro sudo apt install curl sudo apt install net-tools
Line-by-line above :: All-One-Line below
sudo apt update && sudo apt upgrade -f && sudo apt install -y micro curl net-tools --install-recommends -f
Install the “M” of LEMP: the WWW / HTTP Server-side RDBMS = Free MySQL / MariaDB
sudo apt install mariadb-client mariadb-server mariadb-common dbconfig-common | tee ~/LXMP_install_mysql.txt sudo service mariadb start sudo mysql -u root
Line-by-line above :: All-One-Line below
sudo apt install -y mariadb-client mariadb-server mariadb-common dbconfig-common | tee ~/LXMP_install_mysql.txt \ && sudo service mariadb start \ && sudo mysql -u root
MySQL Root Password
TIP: Do NOT create the root password at this step ONLY IF you are certain you want to use the package manager (eg. sudo apt install ) to install phpMyAdmin for graphical, web-based database administration under this, your complete LEMP installation.
The reason to NOT create a root password here, if your intention is to use phpMyAdmin is as follows:
- dbconfig-common will generate a phpMyAdmin friendly, secure password during the package manager install of phpMyAdmin
- dbconfig-common will create the password and install phpMyAdmin without fail, ONLY IF the root password has NOT yet been created.
- The above is in my experience, for a seamless phpMyAdmin install using apt.
- This is not to say that phpMyAdmin can NOT be installed if a ROOT password is created according to instructions in the next step.
- page down a bit to the install phpMyAdmin step of this guide, for more insight.
- dbconfig-common will create the password and install phpMyAdmin without fail, ONLY IF the root password has NOT yet been created.
IF you DO NOT PLAN TO install phpMyAdmin, the please proceed with the next step, and create the ROOT password for MySQL / MariaDB now!
Verify MySQL / MariaDB is installed and functioning: Set MySQL root Password
Based on the previous command above, as advised during the MySQL / MariaDB install, that command was: sudo mysql -u root
where the rdbms is executed using the -u root command, it will launch the db admin on command line, without the need to enter a password [because this is] upon very first launch as system admin on a new install such as this guide is intended.
If everything worked correctly, your shell prompt will change to the MySQL prompt, like so:
MySQL >
The commands shown next will create the create the root user, grant privileges, and set the root password. If you do not plan to use apt and dbconfig to install phpMyAdmin, you can execute the SQL commands now while the MySQL/ MariaDB prompt is active. After executing the SQL then exiting from the database prompt, you’ll continue on the BASH command line and complete the LEMP stack / NGINX PHP MySQL HTTP web server installation by configuring a few things for your new NGINX and PHP installation.
If you followed the steps above to install mysql/ mariadb, you should now be at the command line prompt:
MySQL>
SET PASSWORD FOR 'root'@'localhost' = PASSWORD('*M*y*P*a*s*s*');
In addition to setting the password for the ROOT user, it’s recommended to create a secondary user and assign a password for it. Maybe you have a username that you typically use for development purposes (e.g. a user you tend to place in your wp-config.php file during development to connect to your db server). Create the second MySQL/ MariaDB user with the following command:
GRANT ALL ON *.* TO 'USER02'@'%' IDENTIFIED BY '*U*S*R*2*pw*';
FLUSH PRIVILEGES; exit;
Remember: when issuing commands for MySQL User admin where the server name is required (e.g. as above, user is identified by 'root'@'localhost'
), if you suspect you’ll need to admin something other than ‘localhost’, but you’re not sure and wish to try being universal, you might instead use '
root'@'%'
where % is a wildcard meant to match any server name.
Install NGINX and PHP
Install NGINX and PHP (including a few PHP extension libraries which may be supported by your WordPress version). All of that can be done by executing the following one-line entry from the BASH command line. Take note of the PHP version numbers scrolling up your screen during installation (e.g. 7.4, 8.2, 8.3). You’ll need that number in a moment, after the following command completes for you:
sudo apt install -y nginx php-fpm php-curl php-imagick php-zip php-xml php-mysqli php-pdo php-gd php-mbstring php-xdebug php | tee ~/LXMP_install_nginx-php.txt && php -v | tee -a ~/LXMP_install_nginx-php.txt
Configure new NGINX server_name
Go to NGINX config folder and enable sites:
NGINX stores site-specific configuration files, similar to the way Apache uses separate files for VHOST configurations. The folder names are even the same (e.g. sites-available; sites-enabled). Here, we use a shortcut to save 2 seconds on typing (the asterisk) the path to “/etc/nginx/sites-available
“.
cd /etc/nginx/sites-av* ls -laX
user@system:/etc/nginx/sites-available$ ls -laX total 12 -rw-r--r-- 1 root root 2412 May 16 15:35 default drwxr-xr-x 2 root root 4096 May 16 15:24 . drwxr-xr-x 8 root root 4096 May 16 15:24 ..
Open the NGINX default site file in your text editor. Note that it has a section at the bottom which is commented out. Basically you need to duplicate this file to a new my-site-details file and edit to suit your system folder structure:
sudo micro default
# Add index.php to the list if you are using PHP index index.html index.htm index.nginx-debian.html; server_name _; location / { # First attempt to serve request as file, then # as directory, then fall back to displaying a 404. try_files $uri $uri/ =404; } # pass PHP scripts to FastCGI server # #location ~ .php$ { # include snippets/fastcgi-php.conf; # # # With php-fpm (or other unix sockets): # fastcgi_pass unix:/run/php/php7.4-fpm.sock; # # With php-cgi (or other tcp sockets): # fastcgi_pass 127.0.0.1:9000; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /.ht { # deny all; #} } # Virtual Host configuration for example.com # # You can move that to a different file under sites-available/ and symlink that # to sites-enabled/ to enable it. # #server { # listen 80; # listen [::]:80; # # server_name example.com; # # root /var/www/example.com; # index index.html; # # location / { # try_files $uri $uri/ =404; # } #}
While the following is essentially a matter of removing the comments from the top part of the ./sites-available/default
file, we recommend you invest some time to develop an understanding of the structure of this file. Hint: the location paths are relative to the path designated in this example at the line “root /var/www/html;”, and the lines with “location ~” tell NGINX how to handle paths containing matches to the expression which precedes the opening brace. In my case, I might summarize in Plain English that the syntax “location ~ .(php|phtml)$ /
” means “anything may precede \.php OR \.phtml“, and just like most regular expressions, the “$
” represents the end of allowed characters for that line. If there is a match to \.php or \.phtml, allow the connection and continue passing the file to the PHP-FPM.
The following site configuration file is copied directly from the Debian distro installed on my Windows Subsystem for Linux (WSL):
server { listen 80; server_name mx23ahs; root /var/www/html; index index.php index.phtml index.html index.htm; location / { try_files $uri $uri/ =404; } location ~ .(php|phtml)$ { fastcgi_pass unix:/run/php/php8.2-fpm.sock; include snippets/fastcgi-php.conf; } # HANDLE APACHE .htaccess FILES etc location ~ /.ht { deny all; } # UNCOMMENT TO ALLOW LARGE UPLOADS SUCH AS WORDPRESS .SQL INTO PHPMYADMIN # client_max_body_size 100M; # UNCOMMENT TO AUTO INDEX CONTAINERS MISSING INDEX FILES LIKE index.html index.php etc # autoindex on; # autoindex_exact_size off; # autoindex_format html; # autoindex_localtime on; }
Ensure the line with php/php8.2-fpm.sock
corresponds to your correct installed PHP version. Above it shows php 8.2, but you might have installed a different version. The line you need to look for starts with fastcgi_pass
. If you’re uncertain about your installed php version, just go to your terminal and type:
php -v
I saved my edited file name thinking about “WSL Debian”, so “wsldebian” for the name of the NGINX configuration file works for me. Yours will likely be different.
user@system:/etc/nginx/sites-available$ ls -laX total 16 -rw-r--r-- 1 root root 1296 May 16 16:33 default -rw-r--r-- 1 root root 348 May 16 16:33 wsldebian drwxr-xr-x 2 root root 4096 May 16 15:39 . drwxr-xr-x 8 root root 4096 May 16 15:24 ..
Use the dot-dot-slash + wildcard syntax to quickly switch the current working directory from ./sites-available
to ./sites-enabled
:
cd ../sites-en*
user@system:/etc/nginx/sites-enabled$ ls -laX total 8 lrwxrwxrwx 1 root root 34 May 16 15:24 default -> /etc/nginx/sites-available/default drwxr-xr-x 2 root root 4096 May 16 16:45 . drwxr-xr-x 8 root root 4096 May 16 15:24 ..
Note the symbolic link which exists as part of the default NGINX configuration. Now we create a new symbolic link to point to our new custom file. The “create symbolic link” syntax looks like this: ln -s source_file symbolic_link
. So, in my case, I include the full file path to the ./sites-available folder where the actual file exists as the first argument. Then second argument (the link name) doesn’t need the folder paths because it’s just the name of the link itself and I want the new symbolic link to be in the ./sites-enabled folder so NGINX automatically recognizes it as a VHOST i want to enable, like so:
sudo ln -s /etc/nginx/sites-available/wsldebian wsldebian
Important: Don’t copy/ paste and execute the symbolic link code above, unless you plan to name your server configuration file the same as mine (i.e. wsldebian). You need to use whatever filename you created in the previous step, otherwise the ln -s command will fail or link to a nonexistent file.
If you followed the guide correctly so far, the ./sites-enabled directory listing should now show the complete link to the nginx server conf file you just created. The new link should look just like the “default” link which should exist from the NGINX installation:
user@system:/etc/nginx/sites-enabled$ ls -laX total 8 lrwxrwxrwx 1 root root 34 May 16 15:24 default -> /etc/nginx/sites-available/default lrwxrwxrwx 1 root root 36 May 16 16:45 wsldebian -> /etc/nginx/sites-available/wsldebian drwxr-xr-x 2 root root 4096 May 16 16:45 . drwxr-xr-x 8 root root 4096 May 16 15:24 ..
One more stop for the NGINX virtual host configuration files. Let’s do away with the reference to the “default” site. Since it’s in the sites-enabled folder by way of a symlink (symbolic link), we’ll just break that link.
sudo unlink default
Unlike Apache, NGINX simply scans the ./sites-enabled folder for server configuration files in the ./sites-enabled folder , so you don’t have run a command (e.g. like a2ensite) as in Apache. Just restart NGINX and the new site should be enabled.
You may have to add some lines to the /etc/nginx/nginx.conf
file for file upload size, auto index, file extension information, etc. but this should get you started.
Install phpMyAdmin (optional)
sudo apt install phpmyadmin -f | tee ~/LXMP_install_phpmyadmin.txt # # i prompted to choose between Apache or lighttpd, ignore and choose neither (hit next). # When prompted for a phpmyadmin password, hit [enter] to let the system choose it randomly with dbconfig which you installed with MariaDB above
Important: Here we are asking the terminal to request a password as part of the command to execute mysql (or mariadb) itself. A root password has not yet been set. This approach sets the password for root (as was passed by mysql -u root) with whatever I entered at the prompt. Even though the password has been set to what was entered on the command line, let’s go ahead and use the mysql command to specify the password for posterity.
mysql -u root -p # if MySQL / MariaDB prompt appears then you know you have successfully set the password. # now enter the following at the MySQL prompt while logged in as root: MySQL [(MariaDB)]> SET PASSWORD FOR 'root'@'localhost' = PASSWORD('*M*y*P*a*s*s*'); MySQL [(MariaDB)]> GRANT ALL ON *.* TO '_your_new_user_'@'%' IDENTIFIED BY '*U*s*e*r*-*P*a*s*s'; MySQL [(MariaDB)]> FLUSH PRIVILEGES; MySQL [(MariaDB)]> exit
The MySQL/ MariaDB commands above set the password for the MySQL root user and creates a user “your_new_user” and sets the password for that user with the “IDENTIFIED BY” syntax. You should be familiar with these basic MySQL terminal commands.
TIP: If encounter an error that you can’t log in to MySQL/ MariaDB (e.g. the db prompt doesn’t appear), you might have to start over. Issuing a command to completely remove mysql and the associated configuration files allows you to do that. You don’t have to purge it the installation! Try the following commands:
sudo service mariadb stop mysqld_safe --skip-grant-tables & mysql -u root use mysql; update user set password=PASSWORD("*YOUR*DESIRED*ROOT*PASSWORD*") where User='root'; flush privileges; exit service mariadb restart
Try the instructions above first. If it fails, try the “purge” option of the package manager, like so:
sudo apt purge mysql-common
Configure PHP-FPM
NGINX Won’t Serve PHP Files
If you made it this far, and have tried viewing files on your new NGINX localhost server only to be met with a 502 Error, Bad Gateway, 404, 403 or other HTTP error, you might need to do a little bit of research.
Regardless of your success at this point, you really should read the official NGINX documentation about the “PHP FastCGI” configuration. If you’re having problems, you definitely need to read it!
If you’re having any issues with serving specific file types such as .phtml or other less popular PHP framework based file extension, you’ll need to locate the php fpm configuration specific file, /etc/php/[version.number]/fpm/pool.d/www.conf
(e.g. /etc/php/8.2/fpm/pool.d/www.conf | | /etc/php/7.4/fpm/pool.d/www.conf)
sudo micro /etc/php/8.2/fpm/pool.d/www.conf
Where you might need to uncomment or modify the following line of the www.conf
file:
security.limit_extensions = .php .phtml .phar .html .htm
Reference the NGINX bible.
Still having difficulty viewing pages under your new NGINX server? This off-site link might provide useful insight as well if you continue to encounter “Access Denied” errors related to NGINX and PHP-FPM. The example provided there, based on an Apache reference might help you o examine the syntax used in the configuration files more closely.
Finally, have a look in the Notes section here for more details including a link to an .htaccess to NGINX conversion tool. Gain some insight there for how you might wish to modify your ./sites-available files to best suit your needs. I haven’t investigated it personally, but you might what to check out Winginx.
Install WordPress with NGINX (Debian/ Ubuntu)
We are ready to do this! Now, “Go on! Get!”
One of the great things about Linux is the ability to do just about anything from the BASH Command Line. In most cases, this means you can simply copy/ paste instructions (like the ones found here) onto your own command line, execute the commands, and the result should be the same as the author experienced while writing the tutorial. That said, you should be able to simply copy the following into your terminal to install WordPress.
This is the easiest and fastest way to install WordPress:
sudo mkdir -p /var/www/html/wpcurl --verbose sudo chown www-data: /var/www/html/wpcurl --verbose sudo chmod 755 -R /var/www/html/wpcurl --verbose cd /var/www/html/wpcurl curl https://wordpress.org/latest.tar.gz | sudo -u www-data tar zx -C /var/www/html/wpcurl sudo mv ./wordpress/* . --verbose ## this line moves everything into the wpcurl directory
Note: the cURL operation is combined with a tar operation to extract the downloaded archive. In this illustrated example, the tar archive extraction creates a folder (“wordpress”, the same folder that’s inside of the archive retrieved by cURL) at “/var/www/html/wpcurl/wordpress“. The final line ( sudo mv
… ) moves the files into the directory which was created in the first line. You can safely delete the ./wordpress directory. However, if you want to keep the directory structure as is after the cURL download / tar extraction operations (e.g. ./wpcurl/wordpress ), simply omit the last line which begins with sudo mv
.
The only other required operation at this point is the creation of a database so WordPress knows what to work with during the famous 5-minute installation. If you recall, part of the WordPress installation requires you have a database name, and database user with the correct permissions as WP writes the info into the wp-config.php file which becomes part of your core installation under the WordPress base URL.
If you didn’t install phpMyAdmin yet still need a simple way to manage MySQL / MariaDB , try Adminer. Adminer requires no more installation than to copy a single file into the root of your HTTP server directory (.e.g /var/www/html/adminer.php).
Leave a Reply