Setting up WordPress on Raspberry Pi 3 with Raspbian Stretch Lite, Nginx, MariaDB and PHP 7 as the LEMP stack
Raspbian Stretch was released on 17th August 2017 and this will mean that we will be able to get a variant of Debian 9 on our Raspberry Pi. With Raspbian Stretch, we will be able to run WordPress or any PHP framework with PHP 7.0 which Zend had indicated a performance boost of up to two folds as compared to PHP 5.6.
Just like the benefits that blogging brings to a programmer, the performance boost that PHP 7 brings about is a good reason for me to port my blog over to PHP 7.
Before porting my blog over to PHP 7, it will make sense for me to perform a little proof of concept on my Raspberry Pi 3 first. With Raspbian Stretch Lite, I can see for myself that my blog runs well with PHP 7.0 before spawning a new Digital Ocean instance for Techcoil.
This post documents how I setup an instance of WordPress on Raspberry Pi 3 with Raspbian Stretch Lite, Nginx, MariaDB and PHP 7 as the LEMP stack.
Hardware List to build my Raspberry Pi 3 LEMP server
I used the following hardware for my Raspberry Pi 3 LEMP server:
- Official Raspberry Pi 3 Case - Red/White
- Raspberry PI 3 Model B A1.2GHz 64-bit quad-core ARMv8 CPU, 1GB RAM
- Kingston Digital 32GB microSDHC Class 10 UHS-I 45R Flash Card (SDC10G2/32GBSP)
- My old Samsung Note charger
- AmazonBasics RJ45 Cat-6 Ethernet Patch Cable - 3 Feet (0.9 Meters)
Setting up Raspbian Stretch Lite with SSH server enabled on my microSD card
Once I had gathered all the necessary hardware, proceed to setup Raspbian Stretch Lite with SSH server enabled on my microSD card. Doing so allowed me to SSH into my Raspbian Stretch Lite to perform further configurations.
Assembling the hardware for the Raspberry Pi LEMP Server
Next, I removed my microSD card from my SD card reader and inserted it to the microSD card slot on the Raspberry Pi 3 board. After that, I went ahead to assemble the Raspberry Pi 3 board to the Official Raspberry Pi case.
Starting the Raspbian Stretch Lite operating system
With the assembly of my Raspberry Pi 3 board and Official Raspberry Pi case, I connected one end of the RJ45 cable to the RJ45 port on my Raspberry Pi 3 board and the other end of the cable to one of the switch port of my home router. After that, I connected my micro USB cable and supply power to my Raspberry Pi 3 board.
Changing default password, Locale and Timezone of my Raspbian Stretch Lite
There are a few configurations that we should perform on the first run of our Raspbian Stretch Lite. Therefore I proceeded on to change the default password, Locale and Timezone of Raspbian Stretch Lite.
Installing Nginx on Raspbian Stretch Lite
After configuring the locale settings, I proceeded to install Nginx on my Raspbian Stretch Lite. To do so, I entered the following command:
sudo apt-get update sudo apt-get install nginx -y
After the installation had completed, I ran the following command to verify that the installation was successful:
systemctl status nginx.service
The command returned me with the following output to tell me that Nginx was installed successfully on my Raspbian Stretch Lite:
● nginx.service - A high performance web server and a reverse proxy server Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled) Active: active (running) since Sat 2017-09-23 07:25:05 UTC; 7min ago Docs: man:nginx(8) Process: 15356 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS) Process: 15353 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS Main PID: 15357 (nginx) CGroup: /system.slice/nginx.service ├─15357 nginx: master process /usr/sbin/nginx -g daemon on; master_process on; ├─15358 nginx: worker process ├─15359 nginx: worker process ├─15360 nginx: worker process └─15361 nginx: worker process Sep 23 07:25:05 raspberrypi systemd[1]: Starting A high performance web server and a reverse proxy server... Sep 23 07:25:05 raspberrypi systemd[1]: Started A high performance web server and a reverse proxy server.
Installing MariaDB database server and command line client on my Raspbian Stretch Lite
After I had gotten Nginx on my Raspbian Stretch Lite, I then proceeded on to install the MariaDB database server and the command line client on my Raspbian Stretch Lite. To do so, I ran the following command:
sudo apt-get install mariadb-server mariadb-client -y
Unlike my previous experience with installing mysql servers, this installation of mariadb-server
did not prompt me for a root password. This was because the root user was by default set to use the unix_socket
plugin, which allows the user to use operating system credentials when connecting to MariaDB via Unix socket.
Since the pi user of my Raspbian Stretch installation was configured to use sudo without password prompt, I could get into my mariadb-server
through the following command without supplying any password:
sudo mariadb
Installing PHP 7 on my Raspbian Stretch Lite
Next item on the list was to install the components needed to run WordPress. For that, I needed PHP7, PHP7 Fast CGI Process Manager and PHP7 MySQL bridge to be installed. To install these three items, I ran the following command:
sudo apt-get install php7.0 php7.0-fpm php7.0-mysql -y
After the installation had completed, I entered the following command to check the status:
systemctl status php7.0-fpm.service
Seeing the following output assured me that my PHP Fast CGI Manager had started successfully:
● php7.0-fpm.service - The PHP 7.0 FastCGI Process Manager Loaded: loaded (/lib/systemd/system/php7.0-fpm.service; enabled; vendor prese Active: active (running) since Sat 2017-09-23 07:59:21 UTC; 1min 54s ago Docs: man:php-fpm7.0(8) Main PID: 12314 (php-fpm7.0) Status: "Processes active: 0, idle: 2, Requests: 0, slow: 0, Traffic: 0req/se CGroup: /system.slice/php7.0-fpm.service ├─12314 php-fpm: master process (/etc/php/7.0/fpm/php-fpm.conf) ├─12315 php-fpm: pool www └─12316 php-fpm: pool www
Getting a copy of WordPress
Once I had completed installation of the required components to run WordPress on Raspbian Stretch Lite, I proceeded to get a copy of WordPress.
To do so, I ran the following commands:
cd /var/www sudo wget https://wordpress.org/latest.tar.gz sudo tar xvfz latest.tar.gz sudo rm latest.tar.gz sudo mv wordpress my_new_wordpress_site
Doing so would leave a my_new_wordpress_site
folder inside the /var/www
folder. The my_new_wordpress_site
folder will contain the codes necessary to run WordPress.
Changing the owner of my_new_wordpress_site folder to www-data
In order for media upload to work, the www-data
user needs to owned the my_new_wordpress_site
folder. Hence, the next step was to change the ownership of the my_new_wordpress_site
folder to the same user that runs PHP Fast CGI Manager:
sudo chown -R www-data:www-data /var/www/my_new_wordpress_site
Configuring Nginx to proxy HTTP requests to the PHP 7 Fast CGI Manager
Next, I went on to configure Nginx to serve as a reverse proxy server for my WordPress site. Referencing the informative guide on configuring nginx for a PHP web application like WordPress, I created /etc/nginx/sites-enabled/anewwebsite.com.conf
:
# WordPress single site rules. # Designed to be included in any server {} block. # Upstream to abstract backend connection(s) for php upstream php { server unix:/run/php/php7.0-fpm.sock; } server { listen 80; ## Your website name goes here. server_name anewwebsite.com www.anewwebsite.com; ## Your only path reference. root /var/www/my_new_wordpress_site; ## 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.conf; fastcgi_intercept_errors on; fastcgi_pass php; fastcgi_buffers 16 16k; fastcgi_buffer_size 32k; } location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ { expires max; log_not_found off; } }
Once I had created the configuration file, I got Nginx to take in the configurations by restarting it with the following command:
sudo systemctl restart nginx.service
Creating a MariaDB user and a database instance for the WordPress site
Next up was to create the necessary items for WordPress to use the MariaDB database. It is good practice to create a separate database user for the WordPress site to interact with the database. As such, the next step that I did was to create a Maria DB user that only have the privileges to interact with a particular database instance.
To do so, I first got into the mariadb-server
using mariadb
with the root user:
sudo mariadb
After keying in my password for that root user, I first ran the following command to create a new database instance:
CREATE DATABASE newWordPressDb;
With that database instance created successfully, I then created the MariaDB user with the relevant privileges for accessing that database instance:
CREATE USER 'anewuser'@'localhost' IDENTIFIED BY 'password'; GRANT ALL ON newWordPressDb.* TO 'anewuser'@'localhost';
With that, I would have a MariaDB user for my WordPress site to use a MariaDB database instance.
Setting cgi.fix_pathinfo = 0 in php.ini of PHP 7 Fast CGI Manager
Since WordPress suggested that we should have "cgi.fix_pathinfo = 0;" in php.ini, the next step that I did was to do that.
The php.ini
file for PHP 7 Fast CGI Manager is found in the /etc/php/7.0/fpm
folder. With that, I used nano to open up /etc/php/7.0/fpm/php.ini
and replaced:
;cgi.fix_pathinfo = 1;
with:
cgi.fix_pathinfo = 0;
I then restarted the PHP 7 Fast CGI Manager with the following command:
sudo systemctl restart php7.0-fpm.service
Creating the WordPress configurations to use the database instance
With the MariaDB user and database instance in place, the next thing that I did was to create the WordPress configurations for WordPress to use the database instance.
To do so, I first renamed /var/www/my_new_wordpress_site/wp-config-sample.php
to /var/www/my_new_wordpress_site/wp-config.php
:
sudo mv /var/www/my_new_wordpress_site/wp-config-sample.php /var/www/my_new_wordpress_site/wp-config.php
I then used my browser to access https://api.wordpress.org/secret-key/1.1/salt/ to generate some key and salt values for my /var/www/my_new_wordpress_site/wp-config.php
.
With that, I opened up /var/www/my_new_wordpress_site/wp-config.php
with nano and updated some sections of the file to look like the following:
// ** MySQL settings - You can get this info from your web host ** // /** The name of the database for WordPress */ define('DB_NAME', 'newWordPressDb'); /** MySQL database username */ define('DB_USER', 'anewuser'); /** MySQL database password */ define('DB_PASSWORD', 'password'); /** MySQL hostname */ define('DB_HOST', 'localhost'); /** Database Charset to use in creating database tables. */ define('DB_CHARSET', 'utf8'); /** The Database Collate type. Don't change this if in doubt. */ define('DB_COLLATE', ''); /**#@+ * Authentication Unique Keys and Salts. * * Change these to different unique phrases! * You can generate these using the {@link https://api.wordpress.org/secret-key/1.1/salt/ WordPress.org secret-key service} * You can change these at any point in time to invalidate all existing cookies. This will force all users to have to log in again. * * @since 2.6.0 */ define('AUTH_KEY', '3nGgSDEQkxEnCPDi,l/v?=bx@9KN`OmB?/g=:Adj+Nn<sR+6iH_Q2Ch&-=An.tU!'); define('SECURE_AUTH_KEY', 'l]iOF-zG.%p+A|C3}?2-f<vCu*ar|dA:_[/h&<X~1os=}S:[$.]z[2m#Yto>g:T='); define('LOGGED_IN_KEY', 'du-Xi](g:HvJ;yl$I-nj+)B)$lCWI@,[vorWnj,7^g_N|6CCeE=_js%m[.80:+O@'); define('NONCE_KEY', 'T<:<9+%}L1^|1O+hoP{!vRgy-7Z90XJoO(|s =HMYxf;D+@Q|En-3.8c.[fEt%*d'); define('AUTH_SALT', 'OxlI/I4eq_9dQ{C|fm<sN~E=~-2iX}4%A3+1EF$2kV2N6v8V%zU^|31$8@-G^gNf'); define('SECURE_AUTH_SALT', '=+Oo8d*Y(@wqleOK[9sq3Yg*7x}i&0J!k;J#i>8d|YUioee~($Cb2_CL*aeEJLzm'); define('LOGGED_IN_SALT', '$lr:;X8HG3$o<4s%>gM:0SfZ+1cME _VcQv[%.{UK!:E`7EI[:-^aT:sjYZ5h>&x'); define('NONCE_SALT', 'oVm~a@[$7kb?xxv`_tx?MbOlv:~NV7;{FX/NfvCnb}V|u+9ozG&iJ<=rW+{#qY,{'); /**#@-*/
Editing the hosts file to access my WordPress site from my work computer
After putting in the configurations that are necessary for WordPress to function, the next thing that I did was to get my computer to resolve the domains anewwebsite.com and www.anewwebsite.com with the IP Address of my Raspberry Pi 3: 192.168.1.109. To do so, I opened up /etc/hosts
on my Mac and add in the following entries:
192.168.1.109 anewwebsite.com 192.168.1.109 www.anewwebsite.com
Running the WordPress installation script
Once I had added the necessary hosts entries, I then accessed www.anewwebsite.com with my browser.
Once the installation page appeared, I then keyed in the necessary input:
And clicked the Install WordPress button:
With that, I had WordPress running on Raspberry Pi 3 with Raspbian Stretch Lite, Nginx, MariaDB and PHP 7 as the LEMP stack.
Until I am ready to expose my Raspberry Pi 3 WordPress to the Internet, this setup is sufficient for now.
Further enhancements that I will want to make to my Raspberry Pi 3 WordPress site
When I have clocked enough Raspberry Pi 3 WordPress site in the future, there are some enhancements that will want to make.
Increasing file upload limit
Since the default file upload size limit is slightly less than 1 Megabyte for Nginx and 2 Megabytes for PHP FPM 7, I can only upload slightly less than 1 Megabyte of content to my Raspberry Pi 3 WordPress site.
When I wish to tweak the file upload limit, I can look at how to configure Nginx and PHP 7 stack in Linux to increase or decrease file upload size limit.
Installing a SMTP server for my Raspberry Pi 3 WordPress site to send out emails
When I want to receive emails from my Raspberry Pi 3 WordPress, I will need to install a SMTP server on my Raspberry Pi 3. For this purpose, I can install Postfix as the SMTP server for applications in Raspbian Stretch Lite to send email.
Let's Encrypt my Raspberry Pi 3 WordPress site with a browser-trusted SSL/TLS certificates
Since Google encourages securing our site with HTTPS for better user experience, I should make my Raspberry Pi 3 WordPress communicate in HTTPS when I put it onto the Internet.
When I make my Raspberry Pi 3 WordPress communicate in HTTPS, I can be pretty sure that the communication channel between browsers and my WordPress site is safely encrypted.
Fortunately, Let's Encrypt makes it easy for web masters to deploy secure web applications that serve HTTPS.
Prequisites to serving my Raspberry Pi 3 WordPress site via HTTPS
In a separate post, I had discussed the topic on how to host multiple websites from home. Following those pointers, I will need the following pieces for my Raspberry Pi 3 WordPress site to be accessed from outside my home network with Let's Encrypt browser-trusted certificate:
- A domain or subdomain name that is mapped to the public IP address that my home router had gotten from the Internet Service Provider. If my public IP address changes frequently or you do not have a domain name, you will need to use the service of a Dynamic DNS service. For example, I can buy a Namecheap domain and get my Raspberry Pi 3 to use Namecheap dynamic DNS to update my domain when my home’s public IP address changes.
- Forwarding of network traffic made to port 80 and 443 of my home router public IP address to the IP address of the Raspberry Pi 3 that contains my WordPress site.
Installing Certbot on Raspbian Stretch Lite for obtaining Let’s Encrypt’s browser-trusted certificates
In order to use Let's Encrypt facilities, we will need a ACME client to help us get the SSL artefacts from Let's Encrypt. Therefore, I will need to install Certbot on Raspbian Stretch Lite for obtaining Let’s Encrypt’s browser-trusted certificates.
Configuring Nginx to facilitate Certbot in acquiring the SSL certificate for my domain or subdomain
After installing Certbot, I will need to configure Nginx to facilitate Certbot in acquiring the SSL certificate for my domain. For the purpose of this guide, let's assume that I designate mynewblog.techcoil.com as the domain to reach my WordPress site on my Raspberry Pi 3.
Given these points, I will use nano
to create the Nginx configurations file at /etc/nginx/sites-enabled/mynewblog.techcoil.com.conf
:
sudo nano /etc/nginx/sites-enabled/mynewblog.techcoil.com.conf
After the editor loads the file, I will include the following content in the file:
server { listen 80; root /var/www/my_new_wordpress_site; index index.php; server_name mynewblog.techcoil.com; location ~ /.well-known { allow all; } }
Once I had included the content, I will type Ctrl-X followed by Y to save the file.
Afterwards, I will restart Nginx with the following command:
sudo systemctl restart nginx.service
Using Certbot to get Let's Encrypt to issue browser-trusted SSL certificate for my domain
After Nginx is ready to facilitate Certbot in acquiring the SSL certificate artefacts, I will then run the following command to acquire them:
sudo certbot certonly -a webroot --webroot-path=/var/www/my_new_wordpress_site -d mynewblog.techcoil.com
Generating a strong Diffie-Hellman group
Once Certbot had fetched the SSL certificate artefacts for my domain, I will then generate a Diffie-Hellman group for Nginx to use for exchanging cryptographic keys with its clients:
sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
Updating the Nginx configurations for serving HTTPS for my Raspberry Pi 3 WordPress site
Till this point, I will have the necessary artefacts for Nginx to serve HTTPS. Therefore, I can update the Nginx configurations to use those artefacts in serving HTTPS.
Therefore, I will first use nano
to load /etc/nginx/sites-enabled/mynewblog.techcoil.com.conf
again:
sudo nano /etc/nginx/sites-enabled/mynewblog.techcoil.com.conf
Once the editor loads the file, I will replace its content with the following:
# WordPress single site rules. # Designed to be included in any server {} block. # Upstream to abstract backend connection(s) for php upstream php { server unix:/run/php/php7.0-fpm.sock; } server { listen 80; server_name mynewblog.techcoil.com; return 301 https://$host$request_uri; } # For ssl server { ssl on; ssl_certificate /etc/letsencrypt/live/mynewblog.techcoil.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/mynewblog.techcoil.com/privkey.pem; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; ssl_dhparam /etc/ssl/certs/dhparam.pem; ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'; ssl_session_timeout 1d; ssl_session_cache shared:SSL:50m; ssl_stapling on; ssl_stapling_verify on; add_header Strict-Transport-Security max-age=15768000; default_type application/octet-stream; listen 443; server_name mynewblog.techcoil.com; root /var/www/my_new_wordpress_site; index index.php; location ~ /.well-known { allow all; } 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.conf; fastcgi_intercept_errors on; fastcgi_pass php; fastcgi_buffers 16 16k; fastcgi_buffer_size 32k; } location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ { expires max; log_not_found off; } }
After that, I will type Ctrl-X followed by Y to save the configuration file.
Once I had saved the configuration file, I will run the following command to restart Nginx:
sudo systemctl restart nginx.service
Updating WordPress on the new URL
Once my Nginx server can serve my Raspberry Pi WordPress site with HTTPS, I will then get into MariaDB shell with the following command:
sudo mariadb
When MariaDB shell gets loaded, I will then run the following SQL statements to update WordPress with my new domain name:
UPDATE wp_options SET option_value = replace(option_value, 'http://anewwebsite.com', 'https://mynewblog.techcoil.com') WHERE option_name = 'home' OR option_name = 'siteurl'; UPDATE wp_posts SET guid = replace(guid, 'http://anewwebsite.com','https://mynewblog.techcoil.com'); UPDATE wp_posts SET post_content = replace(post_content, 'http://anewwebsite.com', 'https://mynewblog.techcoil.com'); UPDATE wp_postmeta SET meta_value = replace(meta_value,'http://anewwebsite.com','https://mynewblog.techcoil.com'); UPDATE wp_options SET option_value = replace(option_value, 'http://www.anewwebsite.com', 'https://mynewblog.techcoil.com') WHERE option_name = 'home' OR option_name = 'siteurl'; UPDATE wp_posts SET guid = replace(guid, 'http://www.anewwebsite.com','https://mynewblog.techcoil.com'); UPDATE wp_posts SET post_content = replace(post_content, 'http://www.anewwebsite.com', 'https://mynewblog.techcoil.com'); UPDATE wp_postmeta SET meta_value = replace(meta_value,'http://www.anewwebsite.com','https://mynewblog.techcoil.com');
Cleaning up unnecessary configurations
Since I will be able to access my Raspberry Pi 3 WordPress site with the new URL, https://mynewblog.techcoil.com, I will not be some of the configurations that I had created earlier.
Therefore, I will run the following command to remove the unnecessary Nginx configuration file at /etc/nginx/sites-enabled/anewwebsite.com.conf:
sudo rm /etc/nginx/sites-enabled/anewwebsite.com.conf
Once I had removed the Nginx configurations, I will then restart Nginx with the following command:
sudo systemctl restart nginx.service
After that, I will go to my laptop and remove the following content from /etc/hosts:
192.168.1.109 anewwebsite.com 192.168.1.109 www.anewwebsite.com
Buying the Raspberry Pi 3 hardware to build your own LEMP server to run WordPress
If you do not have the Raspberry Pi 3 hardware mentioned in this post yet, you may want to purchase them from Amazon. Simply click on the button below to add the Raspberry Pi 3 hardware to your cart. You may remove anything that you already have or replace some of the hardware with other hardware.