How to setup WordPress on LEMP with Redis and WP CLI on Debian 11

Are you ready to setup your blazing fast LEMP WordPress? If yes, this guide is for you. Here you will find a full guide with steps

a year ago   •   10 min read

By aquasp
Table of contents

Introduction

If you are looking to self host WordPress, using a LEMP stack is usually one of the best setups that you can do. Apache is good, but nginx offers a better performance while using fewer resources.

Today you will learn, step-by-step, how to put your website online using a fast and secure environment :)

Getting started

As always, the first thing you need is to make your VPS safe, once that is ok, you should update you Debian 11 using:

apt update && sudo apt upgrade

I also recommend you do restart the VPS before the full upgrade. Once that is done, let's clean our VPS.

apt autoremove --purge

Installing all the required packages (MYSQL, PHP-FPM, Redis, Nginx and certbot

MySQL 8

wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb --inet4
dpkg -i mysql-apt-config_0.8.24-1_all.deb

On this screen, you can hit "ok" here:

rm mysql-apt-config_0.8.24-1_all.deb
apt update
apt install mysql-server

Choose a root password and make sure to save it on a safe place:

You can also choose "Use Strong password encryption"

Nginx

apt install nginx
systemctl enable nginx

PHP-FPM

For PHP, the best thing we can do to have the latest versions is using Sury's repository. The easiest way to install is to use his script:

wget -O installer.sh -c https://packages.sury.org/php/README.txt
bash installer.sh
rm installer.sh

Now, you can install any PHP FPM version. The one that I recommend is 8.1 as others are going to lose support soon:

apt install php8.1-fpm*
apt install php8.1-curl php8.1-redis php8.1-mysql php8.1-gd php8.1-intl php8.1-mbstring php8.1-soap php8.1-xml php8.1-xmlrpc php8.1-zip php8.1-imagick
 systemctl enable php8.1-fpm

That will install all the php modules available for 8.1 as well as php-fpm 8.1 itself. This will also start php-fpm when the vps reboots.

Redis

curl -fsSL https://packages.redis.io/gpg | gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg

echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/redis.list

apt update
apt install redis-server
systemctl enable redis-server --now

Certbot

apt install python3-certbot-nginx

WP CLI

wget -c  https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar  --inet4
chmod +x wp-cli.phar
mv wp-cli.phar /usr/local/bin/wp

Creating the database

Before we actually create our first database, it's a great idea to secure it. To do so, use:

mysql_secure_installation

For the first question, use "No":

For the second question, use "No" too:

For everything else, you can just type y and hit enter.

Now, you can safely create your database. To do so, enter on the database as the first thing:

mysql -p

Enter your root mysqlpassword and when you are inside the database, use these commands, replacing the database name and user by something real.

CREATE DATABASE DBNAME;

CREATE USER 'DBUSER'@'localhost' IDENTIFIED BY 'DBPASS';

GRANT ALL ON DBNAME.* TO 'DBUSER'@'localhost';

FLUSH PRIVILEGES;

EXIT;

TL;DR -> On the above, replace:

  • DBNAME with your desired database name
  • DBPASS with your desired password
  • DBUSER with your desired database user

Isolating PHP-FPM

We will already isolate each WP installation on one user, but it's also a great idea to make php-fpm run under the user behalf. That way, PHP won't have root access and will be much harder for a plugin to modify your system using PHP.

To do so, we will need to create our own "php fpm pool":

cd /etc/php/8.1/fpm/pool.d/
cp www.conf yoursitename.conf

Now, enter on your file and edit the following:

  • [www] -> The pool name. Change it to something else. I recommend using the username that you intend to use for the site.
  • user = > Add the user that will run php fpm. Again, use the user that you intend to create
  • group = > put the same thing that you used on user.
  • listen -> On this one, edit the default path by adding a "-youruser" on the end. So after fpm.sock, edit it to fpm-youruser.sock

Since we are already in this file, it's a good idea too to change the PHP from "dynamic" to "ondemand":

There are discussions about which one is the fastest mode for performance, but I've great results with ondemand, specially in terms of ram usage. ondemand is perfect for smaller vps'ses to avoid ram errors and resources issues.

Finally, restart php fpm:

systemctl restart php8.1-fpm

Don't worry, you will get a error (that is expected) since the user that you added there doesn't exists yet. We will fix that soon.

Tuning PHP-FPM

Now, before we setup our user and Redis, it's a great idea to adjust php.ini values. That is pretty easy to do as you can just copy and past all the lines below and hit enter. I'm adding what I consider "optimal" values for WordPress that should ensure a smooth experience by default. Feel free to edit them as you prefer.

sed -i 's/memory_limit = 128M/memory_limit = 1024M/g' /etc/php/8.1/fpm/php.ini

sed -i 's/post_max_size = 8M/post_max_size = 10240M/g' /etc/php/8.1/fpm/php.ini

sed -i 's/upload_max_filesize = 2M/upload_max_filesize = 10240M/g' /etc/php/8.1/fpm/php.ini

sed -i 's/max_file_uploads = 20/max_file_uploads = 1000/g' /etc/php/8.1/fpm/php.ini

sed -i 's/max_execution_time = 30/max_execution_time = 600/g' /etc/php/8.1/fpm/php.ini

sed -i 's/max_input_time = 60/max_input_time = 600/g' /etc/php/8.1/fpm/php.ini

sed -i 's/output_buffering = 4096/output_buffering = off/g' /etc/php/8.1/fpm/php.ini

sed -i 's/;opcache.enable=1/opcache.enable=1/g' /etc/php/8.1/fpm/php.ini

sed -i 's/;opcache.revalidate_freq=2/opcache.revalidate_freq=60/g' /etc/php/8.1/fpm/php.ini

sed -i 's/;opcache.max_accelerated_files=10000/opcache.max_accelerated_files=20000/g' /etc/php/8.1/fpm/php.ini

sed -i 's/;opcache.memory_consumption=128/opcache.memory_consumption=512/g' /etc/php/8.1/fpm/php.ini

sed -i 's/;opcache.interned_strings_buffer=8/opcache.interned_strings_buffer=64/g' /etc/php/8.1/fpm/php.ini

sed -i 's/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/g' /etc/php/8.1/fpm/php.ini

Creating the WordPress nginx configuration

Now that we have our own php fpm pool and we have optimized the necessary settings, we can focus on creating the configuration file for WodPress.

To begin, let's go to one place where nginx reads config files on Debian:

cd /etc/nginx/sites-enabled

Inside this path, create a new config file with your site name and enter on it:

touch mysitename.conf
nano mysitename.conf

Ok so now, you need to copy the code code below, but make sure to replace the following:

  • server unix:/tmp/php-cgi.socket with your actual socket path that we created on this step.
  • server_name domain.tld with your actual domain (e.g: theselfhosting.art)
  • root /home/youruser/public_html;with your real intended user (we will create the user in the next steps, so don't worry)
# Upstream to abstract backend connection(s) for php
upstream php {
        server unix:/tmp/php-cgi.socket;
}

server {
        client_max_body_size 10240M; #This sets upload limit to 10GB
        ## Your website name goes here.
        server_name domain.tld;
        ## Your only path reference.
        root /home/youruser/public_html;
        ## This should be in your http block and if it is, it's not needed here.
        index index.php;

        location = /favicon.ico {
                log_not_found off;
                access_log off;
        }

        location = /robots.txt {
                allow all;
                log_not_found off;
                access_log off;
        }

        location / {
                # This is cool because no php is touched for static content.
                # include the "?$args" part so non-default permalinks doesn't break when using query string
                try_files $uri $uri/ /index.php?$args;
        }

        location ~ \.php$ {
                #NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
                include fastcgi_params;
                fastcgi_intercept_errors on;
                fastcgi_pass php;
                #The following parameter can be also included in fastcgi_params file
                fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
        }

        location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
                expires max;
                log_not_found off;
        }
}

Your code should look similar to this one:

Tuning and configuring Redis

We are close to the end. The last thing we need to do before installing WordPress is to setup Redis. To do so, just copy and paste the following lines:

sed -i 's/port 6379/port 0/g' /etc/redis/redis.conf

line_old='# unixsocket /run/redis.sock'

line_new='unixsocket /var/run/redis/redis-server.sock'

sed -i "s%$line_old%$line_new%g" /etc/redis/redis.conf

sed -i 's/# unixsocketperm 700/unixsocketperm 770/g' /etc/redis/redis.conf

sed -i 's/# maxmemory <bytes>/maxmemory 1024mb/g' /etc/redis/redis.conf

systemctl restart redis-server

This should ensure a great Redis Performance for our WP installation. You can increase the memory later if you have more sites to host.

Creating a new user

Finally, we will be creating the user! Remember when you added the path on the nginx config file? (/home/youruser/public_html)

Now it's time to create this user. Use the same intended user that you have used on the steps before.

Many people just install WordPress directly on the root user, but I think that is a bad idea for production. Using root directly will make things less secure as PHP will run as root and if your site gets hacked somehow, it will be easier to get the full VPS access.

Additionally, if you are using root and hosting more than 1 site, malware in one site usually means malware on all sites as they are all sharing the same file permissions. So for those reasons, I highly recommend that you use 1 user per site and that is really easy to do.

adduser redstonecube

You can leave most information about the user blank.

Now, to make your user easier to use, it's a good idea to customize the bashrc to make you login directly inside public_html when you enter the user. To do so, enter the user:

su  youruser

And then, edit the file .bashrc after entering at your home folder:

cd ~
mkdir public_html
nano .bashrc

Now, on the very top of the file, paste this line:

cd ~/public_html

Now, if you leave your user with "exit" and enter on it again you will be pleased to see that it is on the correct location by default:

Restarting services

Now, before using the user, wp cli and other cool things, you must restart the services. This must be done on the root user:

systemctl restart php8.1-fpm
systemctl restart nginx
systemctl restart redis-server

Installing WordPress

All the hard work will pay off now. This installation will be really easy. Let's do it with WP CLI. Inside your public_html, run:

wp core download

Now, let's create our wp-config.php and then setup our WP:

wp config create --dbname=DBNAME --dbuser=DBUSER --dbpass="DBPASS"

Perfect, next thing, let's setup the actual site (site name, etc):

wp core install --url=https://yoursiteurl.com --title="Your site title" --admin_name=youradminuser --admin_password="yourpass" --admin_email="youremail"

Now it's all good except for the SSL. Let's go back to the root user to setup our beautiful SSL.

Installing and forcing the SSL

Inside our root user, let's run:

certbot

You should see the setup screen, which is really easy to follow. Just input your email, choose the options and certbot will detect your nginx site automatically:

After that, it should be up and running perfectly.

Automatic SSL renewal

A pretty good thing to do is to renew your certificate automatically. To do so, you can just use this command to create a cron job:

crontab -e

Select nano as a option and then put the following cron job:

0 0 * * 0 certbot renew

Save the file, and exit. Your SSL is good to go now and will be automatically renewed, all for 0$/month.

Finishing Redis

Redis is installed at the server side, but it's not enabled at our WordPress installation.

Before going back to your WP user, make sure to add your intended user to the redis group and to add write and read permissions to the sock file:

usermod -aG redis youruser
chmod g+rw /var/run/redis/redis-server.sock

To enable it, switch back to your user:

su youruser

And now, inside public_html, set the Redis database number to 0 and install the plugin to control it:

wp config set WP_REDIS_DATABASE 0;
wp config set WP_REDIS_PATH '/var/run/redis/redis-server.sock'
wp config set WP_REDIS_SCHEME unix
wp plugin install redis-cache --activate
wp redis enable
wp redis update-dropin

Your Redis is good to go now. You should have a smooth wp-admin experience:

Conclusion

There is still a lot that you can do after installing this setup. I'do recommend you to look on my list 7 things that you should do after installing WordPress

You can also install caching plugins to optimize your assets and start to build your business. I hope you have fun. This setup is really solid and fast. GZIP is enabled by default for HTML files, but you can easily add extra rules to nginx in order to. If you have any doubts or questions, drop a comment and I'll try to help as best as I can.

If you enjoyed this article, you can share it your friends or subscribe to The Self Hosting Art to keep us motivated. Thank you for reading :)

You can also help with Monero, Litecoin, Bitcoin or Nano: Monero:837X2SppmrrPkBbpsy9HQU1RFxKhsBcn6GdQv2wR5wGoiw8ctfh6Rt36xaszveZHysYA5KSDBr51y5YuQ3YCV23sJS9nhqW BTC:bc1qrvgz7dzzlfllulakw87vzvtf7s2u8t0sxpjehr Litecoin:ltc1qycz6ssg6xjxttuld6l6ulzqdr3y70rm8wv2g9p Nano:nano_1jmd6dg4dbem7f3wrojr7g45ioe6eb5et3iq11f8urfxe8qausxipup8bhua

Spread the word

Keep reading