Run WordPress+W3TotalCache with LEMP (Nginx, PHP-FPM+APC and MySQL) stack on CentOS 6 VPS for maximum performance

How to Install WordPress+W3TotalCache with LEMP (Nginx, PHP-FPM and MySQL) stack on CentOS 6 VPSNginx is a free, open-source, high-performance HTTP web-server which unlike some other web-servers, it does not rely on threaded handling of the requests but instead it uses a much more scalable event driven (asynchronous) architecture.

This uses a very small and predictable amount of memory under heavy load. Nginx in combination with the simple and very robust FastCGI Process Manager for PHP (PHP-FPM) and the world most popular database server MySQL can give you a lot of power and performance while still using a small memory footprint.

The following article is all about how to install and configure LEMP Stack on a CentOS 6 VPS and host blazing fast WordPress powered web applications.

The article is split in the following sections:

Before proceeding with the installation, fire up a screen session by running:
## screen -U -S lemp-stack

Once you’re in the screen session, make sure your CentOS 6 VPS is fully up-to-date by executing:

## yum update

if you have Apache installed on your VPS, stop it and remove it by executing:

## /etc/init.d/httpd stop
## yum remove httpd
STEP 1) Install and configure Nginx

 

enable the epel repository by running:

find your VPS architecture by running uname -m

– 32bit VPS:

## wget -P /tmp http://mirror.pnl.gov/epel/6/i386/epel-release-6-8.noarch.rpm
## rpm -Uvh /tmp/epel-release-6-8.noarch.rpm
## rm -f /tmp/epel-release-6-8.noarch.rpm

– 64bit VPS:

## wget -P /tmp http://mirror.itc.virginia.edu/fedora-epel/6/x86_64/epel-release-6-8.noarch.rpm
## rpm -Uvh /tmp/epel-release-6-8.noarch.rpm
## rm -f /tmp/epel-release-6-8.noarch.rpm

verify everything is up-to-date

## yum update

Install Nginx via yum by running:

## yum install nginx

Navigate to Nginx’s config directory in /etc/nginx/ and edit nginx.conf with your favorite editor:

## cd /etc/nginx/
## vim nginx.conf
user              nginx;
worker_processes  2;

error_log  /var/log/nginx/error.log;
#error_log  /var/log/nginx/error.log  notice;
#error_log  /var/log/nginx/error.log  info;

pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  30;
    server_tokens off;

    gzip on;
    gzip_disable "MSIE [1-6]\.(?!.*SV1)";
    gzip_http_version 1.1;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript text/x-js;

    # enabled sites
    include /etc/nginx/sites-enabled/*;

}

create sites-enabled and sites-available inside the /etc/nginx directory:

## mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled

set-up default Nginx virtual host directive by adding the following in /etc/nginx/sites-available/default.conf

server {
    listen       80 default_server;
    server_name  _;
    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
    error_page  404              /404.html;
    location = /404.html {
        root   /usr/share/nginx/html;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

enable the default Nginx virtual host directive by creating symbolic link to the default vhost configuration in /etc/nginx/sites-enabled/

## cd /etc/nginx/sites-enabled
## ln -s /etc/nginx/sites-available/default.conf

test Nginx’s configuration, add it to your system startup and finally fire it up by:

## nginx -t
## /etc/init.d/nginx restart
## chkconfig nginx on

STEP 2) Install and configure MySQL

 

install MySQL database server, start and add it to your system start-up by running the following commands:

## yum install mysql mysql-server
## service mysqld restart
## chkconfig mysqld on

next, run the command below in order to set-up MySQL

## mysql_secure_installation
Enter current password for root (enter for none):
Set root password? [Y/n] y
Remove anonymous users? [Y/n] y
Disallow root login remotely? [Y/n] y
Remove test database and access to it? [Y/n] y
Reload privilege tables now? [Y/n] y

make sure your MySQL is not exposed to listen on your server’s public IP by adding the following to /etc/my.cnf

## vim /etc/my.cnf

[mysqld]
bind-address = 127.0.0.1
...

restart the database server for the changes to take effect by:

## /etc/init.d/mysqld restart

STEP 3) Install and configure PHP-FPM

 

install PHP-FPM and some useful PHP extensions by executing the command below:

## yum install php-fpm php-mysql php-gd php-mcrypt

edit /etc/php.ini and change/uncomment the following:

cgi.fix_pathinfo=0
date.timezone = America/New_York
memory_limit = 64M
expose_php = Off

next, edit /etc/php-fpm.conf with your favorite editor and uncomment the following:

emergency_restart_threshold = 10
emergency_restart_interval = 1m
process_control_timeout = 10

with all that in place, set-up a PHP-FPM pool in /etc/php-fpm.d/www.conf:

## mv /etc/php-fpm.d/www.conf /root/
## vim /etc/php-fpm.d/www.conf
[wordpress]
;listen = 127.0.0.1:9001
listen = /var/run/php-wordpress.socket
user = nginx
group = nginx
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/blogcms.log
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 10
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 4
pm.max_requests = 400
listen.backlog = -1
pm.status_path = /status
request_terminate_timeout = 120s
rlimit_files = 131072
rlimit_core = unlimited
catch_workers_output = yes
php_value[session.save_handler] = files
php_value[session.save_path] = /var/lib/php/session
php_admin_value[error_log] = /var/log/php-fpm/wordpress-error.log
php_admin_flag[log_errors] = on

restart PHP-FPM and add it to your system startup:

## /etc/init.d/php-fpm restart
## chkconfig php-fpm on

at this point you should have Nginx, MySQL and PHP-FPM up and running on your server. Proceed with creating a vhost directive for your WordPress application:

## vim /etc/nginx/sites-available/my-wordpress.tld.conf
server {
        listen 80;
        server_name my-wordpress.tld;
        rewrite ^(.*) http://www.my-wordpress.tld$1 permanent;
}

server {
        listen 80;
        server_name www.my-wordpress.tld;

        client_max_body_size 5m;
        client_body_timeout 60;

        access_log /var/log/nginx/my-wordpress.tld-access;
        error_log /var/log/nginx/my-wordpress.tld-error error;

        root /var/www/html/my-wordpress.tld/;
        index  index.html index.php;

        ### root directory ###
        location / {
                try_files $uri $uri/ /index.php?$args;
        }

        ### security ###
        error_page 403 =404;
        location ~ /\. { access_log off; log_not_found off; deny all; }
        location ~ ~$ { access_log off; log_not_found off; deny all; }
        location ~* wp-admin/includes { deny all; }
        location ~* wp-includes/theme-compat/ { deny all; }
        location ~* wp-includes/js/tinymce/langs/.*\.php { deny all; }
        location /wp-includes/ { internal; }
        #location ~* wp-config.php { deny all; }
        location ~* ^/wp-content/uploads/.*.(html|htm|shtml|php)$ {
                types { }
                default_type text/plain;
        }

        #  location ~* wp-admin {
        #      allow <YOUR_IP>;
        #      allow 127.0.0.1;
        #      deny all;
        #  }

        ### disable logging ###
        location = /robots.txt { access_log off; log_not_found off; }
        location = /favicon.ico { access_log off; log_not_found off; }

        ### caches ###
        location ~* \.(jpg|jpeg|gif|css|png|js|ico|html)$ { access_log off; expires max; }
        location ~* \.(woff|svg)$ { access_log off; log_not_found off; expires 30d; }
        location ~* \.(js)$ { access_log off; log_not_found off; expires 7d; }

        ### php block ###
        location ~ \.php?$ {
                try_files $uri =404;
                include fastcgi_params;
                #fastcgi_pass 127.0.0.1:9001;
                fastcgi_pass unix:/var/run/php-wordpress.socket;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_intercept_errors on;
                fastcgi_split_path_info ^(.+\.php)(.*)$;
                #Prevent version info leakage
                fastcgi_hide_header X-Powered-By;
        }
}

enable the virtual host directive and restart nginx by running the following commands:

## cd /etc/nginx/sites-enabled
## ln -s /etc/nginx/sites-available/my-wordpress.tld.conf
## nginx -t
## /etc/init.d/nginx restart

test PHP-FPM by creating PHP info.php script in your vhost document root in /var/www/html/my-wordpress.tld/ :

## mkdir -p /var/www/html/my-wordpress.tld/
## cd /var/www/html/my-wordpress.tld/
##  echo -e "<?php\n\tphpinfo();\n" > info.php

access http://my-wordpress.tld/info.php to test your PHP-FPM

STEP 4) Set-up WordPress

 

Next step is to install WordPress inside your vhost document root in /var/www/html/my-wordpress.tld/. Before installing WordPress, let’s first create a MySQL database by running:

## mysql -u root -p
mysql> create database wordpressDB;
mysql> grant all on wordpressDB.* to wpUser@localhost identified by 'YOUR_PASS';
mysql> quit
## cd /var/www/html/my-wordpress.tld/
## wget http://wordpress.org/latest.zip
## unzip latest.zip
## mv wordpress/* .
## rm -rf latest.zip wordpress/

next, copy WordPress sample config and set-up your MySQL database information:

## cp wp-config-sample.php wp-config.php
## vim wp-config.php
define('DB_NAME', 'wordpressDB');
define('DB_USER', 'wpUser');
define('DB_PASSWORD', 'YOUR_PASS');
## chown nginx: -R /var/www/html/my-wordpress.tld/

open http://my-wordpress.tld and complete your WordPress installation


STEP 5) Set-up caching for best performance

 

Install PHP-APC (Alternative PHP Cache) by running:

## yum install php-pecl-apc

once APC is installed, add the following to /etc/php.d/apc.ini

## cat > /etc/php.d/apc.ini
extension = apc.so
apc.enabled=1
apc.shm_segments=1
apc.shm_size=128M
apc.num_files_hint=1024
apc.user_entries_hint=4096
apc.ttl=7200
apc.use_request_time=1
apc.user_ttl=7200
apc.gc_ttl=3600
apc.cache_by_default=1
apc.filters
apc.mmap_file_mask=/tmp/apc.XXXXXX
apc.file_update_protection=2
apc.enable_cli=0
apc.max_file_size=1M
apc.stat=1
apc.stat_ctime=0
apc.canonicalize=0
apc.write_lock=1
apc.report_autofilter=0
apc.rfc1867=0
apc.rfc1867_prefix =upload_
apc.rfc1867_name=APC_UPLOAD_PROGRESS
apc.rfc1867_freq=0
apc.rfc1867_ttl=3600
apc.include_once_override=0
apc.lazy_classes=0
apc.lazy_functions=0
apc.coredump_unmap=0
apc.file_md5=0
apc.preload_path

and restart PHP-FPM for the changes to take effect.

## /etc/init.d/php-fpm restart

check if APC is loaded by running:

## php -m | grep -w apc

or by opening the info.php script in your document root.

Next thing to do is to login to your WordPress administration and install the W3 Total Cache Plugin. For W3 Total Cache Plugin to work you have to first enable Pretty URLs in

Settings->Permalinks->Custom Structure:

http://my-wordpress.tld/%postname%/

and then proceed with installing the W3 Total Cache. Once installed, go to

Performance->General Settings

and enable/disable the following options:

Page cache: enabled
Page cache method: Disk: Enhaced

Minify: disabled

Database Cache: enabled
Database Cache Method: Opcode: Alternative PHP Cache (APC)

Object Cache: enbabled
Object Cache Method: Opcode: Alternative PHP Cache (APC)

Browser Cache: disabled

CDN: this is up to you.

Click Save all settings to submit the changes.

Add the following in /var/www/html/my-wordpress.tld/nginx.conf

## cat > /var/www/html/my-wordpress.tld/nginx.conf
# BEGIN W3TC Page Cache cache
location ~ /wp-content/cache/page_enhanced.*html$ {
    add_header Vary Cookie;
}
# END W3TC Page Cache cache
# BEGIN W3TC Page Cache core
set $w3tc_rewrite 1;
if ($request_method = POST) {
    set $w3tc_rewrite 0;
}
if ($query_string != "") {
    set $w3tc_rewrite 0;
}
if ($request_uri !~ \/$) {
    set $w3tc_rewrite 0;
}
if ($http_cookie ~* "(comment_author|wp\-postpass|w3tc_logged_out|wordpress_logged_in|wptouch_switch_toggle)") {
    set $w3tc_rewrite 0;
}
if (!-f "$document_root/wp-content/cache/page_enhanced/$http_host/$request_uri/_index.html") {
  set $w3tc_rewrite 0;
}
if ($w3tc_rewrite = 1) {
    rewrite .* "/wp-content/cache/page_enhanced/$http_host/$request_uri/_index.html" last;
}
# END W3TC Page Cache core

make sure the ownership of the document root is ok by:

## chown nginx: -R /var/www/html/my-wordpress.tld/

The next step is to tell Nginx to use this configuration file. Edit /etc/nginx/sites-enabled/my-wordpress.tld.conf and add/uncomment the following:

include /var/www/html/my-wordpress.tld/nginx.conf;
...
location ~* wp-config.php { deny all; }

test Nginx’s configuration file and restart it for the changes to take effect by executing:

## nginx -t
## /etc/init.d/nginx restart

You can also edit your WordPress configuration file /var/www/html/my-wordpress.tld/wp-config.php and define the following so WordPress will not have to query the database for the site url:

define('WP_HOME', 'http://my-wordpress.tld');
define('WP_SITEURL', 'http://my-wordpress.tld');

Of course, if you are one of our Linux VPS Hosting customers, you don’t have to do any of this, simply ask our admins, sit back and relax. Our admins will set this up for you immediately.

PS. If you liked this post please share it with your friends on the social networks using the buttons on the left or simply leave a reply below. Thanks.

7 thoughts on “Run WordPress+W3TotalCache with LEMP (Nginx, PHP-FPM+APC and MySQL) stack on CentOS 6 VPS for maximum performance”

  1. Hi, I had a few pre-sales questions. If I purchase a managed VPS from you, do you handle setting up all these programs for me (i.e. LEMP stack with APC and easy setup of multiple WordPress sites)? Also, what location(s) do you offer for hosting the VPS and what type of connection speed is available to the server? I am wanting to host my clients sites on a fast, reliable & robust VPS. Thanks for providing such a great tutorial.

    Silas

    Reply
  2. Hi,
    I am getting stuck in step 3) unable to install PHP-FPM terminal output follows

    mahinth@mahinthjoe.com [/etc/nginx]# sudo yum install php-fpm php-mysql php-gd php-mcrypt
    Loaded plugins: fastestmirror
    Loading mirror speeds from cached hostfile
    * base: centos.host-engine.com
    * epel: kdeforge2.unl.edu
    * extras: mirrors.unifiedlayer.com
    * remi: mirrors.mediatemple.net
    * remi-php55: mirrors.mediatemple.net
    * updates: mirror.hmc.edu
    Setting up Install Process
    No package php-fpm available.
    No package php-mysql available.
    No package php-gd available.
    No package php-mcrypt available.
    Error: Nothing to do

    Reply

Leave a Comment