
Last time around we made our Debian VPS ready with the LEMP recipe. Let’s now configure the stack and migrate over the old WP website.
What good a website with a “Welcome to nginx” note? That’s where we left last time.
My primary reference for this Apache to Nginx migration was this article — in fact, my configs are more or less a copy-paste from this guide.
For your convenience I’ll just repeat the steps here…
Configuring the Nginx vhostSince it’s always nice to save a backup of the original default config files before we make any changes — because it’s easy to roll back to the reference point and troubleshoot when something goes wrong — we move the original nginx.conf file as follows:
# mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf-orgThen create a new /etc/nginx/nginx.conf file and insert the following text in it:
user www-data; worker_processes 1; pid /var/run/nginx.pid; events { worker_connections 1024; # multi_accept on; } http { sendfile on; tcp_nopush on; tcp_nodelay on; server_tokens off; include mime.types; default_type application/octet-stream; index index.php index.htm index.html redirect.php; #Gzip gzip on; gzip_vary on; gzip_proxied any; gzip_comp_level 6; gzip_buffers 16 8k; gzip_http_version 1.1; gzip_disable "MSIE [1-6].(?!.*SV1)"; gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; #FastCGI fastcgi_intercept_errors on; fastcgi_ignore_client_abort on; fastcgi_buffers 8 16k; fastcgi_buffer_size 32k; fastcgi_read_timeout 120; fastcgi_index index.php; limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; ## # Virtual Host Configs ## include /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-enabled/*; #Our individual site vhost server files will live here }The worker_processes 1 directive above is of special importance here. I have the worker_processes set to 1 simply because the VPS I’m using currently offers me one CPU. It’s usually safe to set the worker_processes number to the number of processor cores you have. Like, for example, under a Rackspace Cloud Server install, I’ll have it set to 4, simply because I’m offered that many virtual cores.
Second, if you notice, the nginx.conf above doesn’t have any WordPress specific configs yet. However, if you look at the last statement, it basically tells Nginx to refer to /etc/nginx/sites-enabled/ directory for these configs.
If you do an ls inside of /etc/nginx/ the default DotDeb install gives your two directories that we all must take note of /etc/nginx/sites-enabled/ as mentioned in our /etc/nginx.conf file, and more importantly /etc/nginx/sites-available/ directory.
This second directory is where we’ll have our vhost configs, while in the former we’ll simply have individual vhost configs files’ symlinks. This gives us the option to disable a site while keeping the original config files intact by simply deleting the symlinks — and Nginx thinks the site is gone.
Do an ls inside /etc/nginx/sites-available/, and you’ll notice a default file already present. This same file is also simlinked inside /etc/nginx/sites-enabled/default. Open this file and you notice a location of a file: /usr/share/nginx/www/index.html. The content of this file follows:
<html> <head> <title>Welcome to nginx!</title> </head> <body bgcolor="white" text="black"> <center><h1>Welcome to nginx!</h1></center> </body> </html>Remember the “Welcome to nginx!” message from the concluding screenshot of part 1? This /etc/nginx/sites-enabled/default file is a useless location, yet an excellent starting point to understand Nginx vhost configurations.
Let us now create our WordPress vhost config — in my case it’s /etc/nginx/sites-available/linuxforu.com — and insert the following text:
server { listen 80; server_name linuxforu.com www.linuxforu.com; root /srv/www/linuxforu.com/public; access_log /srv/www/linuxforu.com/logs/access.log; error_log /srv/www/linuxforu.com/logs/error.log; client_max_body_size 8M; client_body_buffer_size 128k; #The section below contains your WordPress rewrite rules location / { try_files $uri $uri/ /index.php?q=$uri&$args; } location /search { limit_req zone=one burst=3 nodelay; rewrite ^ /index.php; } fastcgi_intercept_errors off; location ~* \.(ico|css|js|gif|jpe?g|png)$ { expires max; add_header Pragma public; add_header Cache-Control "public, must-revalidate, proxy-revalidate"; } #sample 301 redirect # location /2011/06/26/permalink/ { # rewrite //2011/06/26/permalink/ http://example.com/2011/06/27/permalink_redirecting_to/ permanent; # } #Send the php files to upstream to PHP-FPM #This can also be added to separate file and added with an include location ~ \.php$ { try_files $uri =404; #This line closes a big security hole #see: http://forum.nginx.org/read.php?2,88845,page=3 fastcgi_param QUERY_STRING $query_string; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length; fastcgi_param SCRIPT_NAME $fastcgi_script_name; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param REQUEST_URI $request_uri; fastcgi_param DOCUMENT_URI $document_uri; fastcgi_param DOCUMENT_ROOT $document_root; fastcgi_param SERVER_PROTOCOL $server_protocol; fastcgi_param GATEWAY_INTERFACE CGI/1.1; fastcgi_param SERVER_SOFTWARE nginx; fastcgi_param REMOTE_ADDR $remote_addr; fastcgi_param REMOTE_PORT $remote_port; fastcgi_param SERVER_ADDR $server_addr; fastcgi_param SERVER_PORT $server_port; fastcgi_param SERVER_NAME $server_name; fastcgi_pass 127.0.0.1:9000; } location /wp-admin { auth_basic "Administrator Login"; auth_basic_user_file /srv/www/linuxforu.com/.htpasswd; } #!!! IMPORTANT !!! We need to hide the password file from prying eyes # This will deny access to any hidden file (beginning with a .period) location ~ /\. { deny all; } #Once you have your w3-total.conf file ready uncomment out the line below include w3-total.conf; }—————–
Now, let’s quickly go through the essential portions of this file.
With that done. Let us create our webroot folder structure. Note that we’re not configuring it inside /usr/share/nginx/www/, but under /srv/. Under a fresh Debian install, this directory is empty.
# mkdir -p /srv/www/linuxforu.com/{public,logs}So, now we have /srv/www/linuxforu.com/public/, which will be our webroot for linuxforu.com, and /srv/www/linuxforu.com/logs/, where Nginx saves the access and error logs.
Finally let’s create the /etc/nginx/w3-total.conf file that we’ll need later (as we defined in the vhost config above).
# touch /etc/nginx/w3-total.confMigration timeTime to migrate the wordpress files from the Apache server…
Note that if you’re configuring the Nginx server on the same host, you simply need to point to the correct WordPress install directory in the vhost config file above in line #5 and 6.
Since, for me, it was from MediaTemple to this VPS, and also since MediaTemple gives me shell access with rsync facility, it was a simple recursive rysnc for me.
In case you don’t have shell access to your old server, or your host doesn’t provide you with rsync facility, the the job becomes a bit tiresome — that is, having to download all the files over FTP. I somehow don’t like FTP, so I don’t touch shared hostings facilities that don’t provide me with rsync.
rysnc --progress -ruvpa <apache-server-username>@linuxforu.com:path/to/webroot/ /srv/www/linuxforu.com/public/Maybe you can take a latest mysqldump of database before running the rsync command above — a plugin like WP-DBManager is a handy resoruce for the same. So, what happens in this case is, you rsync over the latest DB dump along with the other files.
Refer back to the /etc/nginx/nginx.conf file — note that the nginx daemon runs as www-data:www-data. However, our webroot — /srv/www/linuxforu.com/public — is root:root. Let’s correct that so that Nginx/WordPress has no issues writing to the webroot.
chown -R www-data:www-data /srv/www/linuxforu.com/publicRemeber, W3 Total Cache also needs to write its configurations, so we change the ownership of this file too:
chown www-data:www-data /etc/nginx/w3-total.confAll good.
Now that the WordPress files are in place, type to generate the password to secure the /wp-admin/ directory. The easier way is to simply install the htpadded utility, part of the apache2-utils package
# apt-get install apache2-utilsNo need to panic, this package doesn’t pull the main Apache server as a dependency.
Generate the htpasswd:
htpasswd -c /srv/www/linuxforu.com/.htpasswd <htpassds-username>The location of the .htpasswd file could be anywhere as long as it corresponds with the location mentioned in the vhost file.
Database import and MySQL tuningAlthough you can drive the website without altering any settings in MySQL, the following tunings are handy if you have a 2GB RAM VPS.
Let’s first save a backup of the default /etc/mysql/my.cnf file:
mv /etc/mysql/my.cnf /etc/mysql/my.cnf-bakNow create an empty /etc/mysql/my.cnf file and append the following directives:
[client] port = 3306 socket = /var/run/mysqld/mysqld.sock [mysqld_safe] socket = /var/run/mysqld/mysqld.sock nice = 0 [mysqld] user = mysql pid-file = /var/run/mysqld/mysqld.pid socket = /var/run/mysqld/mysqld.sock port = 3306 basedir = /usr datadir = /var/lib/mysql tmpdir = /tmp language = /usr/share/mysql/english skip-external-locking key_buffer = 16M max_allowed_packet = 16M thread_stack = 192K thread_cache_size = 16 myisam-recover = BACKUP max_connections = 100 table_cache = 256 thread_concurrency = 16 query_cache_limit = 4M query_cache_size = 128M general_log_file = /var/log/mysql/mysql.log general_log = 1 log_slow_queries = /var/log/mysql/mysql-slow.log long_query_time = 2 log-queries-not-using-indexes expire_logs_days = 10 max_binlog_size = 100M [mysqldump] quick quote-names max_allowed_packet = 16M [isamchk] key_buffer = 16M # * IMPORTANT: Additional settings that can override those from this file! # The files must end with '.cnf', otherwise they'll be ignored. # !includedir /etc/mysql/conf.d/We’ve just ended up supplying some good default memory power to our MySQL server.
Time to import the database — but first, we need to create the database to import the data into:
# mysql -u root -p Enter password:Enter the root password that you’ve set when you installed the mysql-server-5.5 packages in the earlier article. Once you get the mysql> prompt after authentication, create a new MySQL user and a database on which this user has rights:
mysql> CREATE DATABASE lfydb; Query OK, 1 row affected (0.00 sec) mysql> GRANT ALL PRIVILEGES ON lfydb.* TO "lfy_user_name"@"localhost" IDENTIFIED BY "password"; Query OK, 0 rows affected (0.00 sec) mysql> FLUSH PRIVILEGES; Query OK, 0 rows affected (0.01 sec) mysql> EXITNow import the current database — if you used a plugin like WP-DBManager with it’s default settings, the location should be something like this: /srv/www/linuxforu.com/wp-content/backup-db/1328724761_-_databasename.sql
Import this dump into your freshly created database as follows:
mysql -u lfy_user_name -p -h localhost lfydb < /srv/www/linuxforu.com/public/wp-content/backup-db/1328724761_-_databasename.sqlIt will ask for lfy_user_name’s password. It will take some time before returning you the shell prompt — depends on the size of the database.
Finally, open your /srv/www/linuxforu.com/public/wp-config.php file, and make sure the database name, user, password and hostname correspondent to the new DB we created and imported the dump onto just now:
// ** MySQL settings - You can get this info from your web host ** // /** The name of the database for WordPress */ define('DB_NAME', 'lfydb'); /** MySQL database username */ define('DB_USER', 'lfy_user_name'); /** MySQL database password */ define('DB_PASSWORD', 'password'); /** MySQL hostname */ define('DB_HOST', 'localhost');All good — reload mysql service:
# /etc/init.d/mysql reloadBack on WordPress to make final changesLet us enable our vhost first — remember we need to create the correct symlink:
ln -s /etc/nginx/sites-available/linuxforu.com /etc/nginx/sites-enabled/linuxforu.comReload nginx service with the new config:
/etc/init.d/nginx reloadTime to now check the site! However, before making changes permanent to your domain’s DNS service, we will edit our local machine’s (not the VPS — but the desktop or laptop we’re using) /etc/hosts file to cheat the browser into bypassing a DNS check to open linuxforu.com:
<Nginx-VPS-IP-ADDRESS> linuxforu.com www.linuxforu.comSave and close… run a ping test on your local machine to confirm that linuxforu.com resolves to the IP address of the Nginx VPS.
Time to finally launch your browser, and login to the wordpress backend — upon entering the www.your-domain-name-here.com/wp-admin/ you should be greeted by the htpasswd authentication first :-)

Web server directory access authentication for accessing /wp-admin/
Validate, and you get your WordPress login page. Once you authenticate, first thing to do is add the nginx Compatibility plugin. Quoting the plugin’s page will make it clear why this is necessary:
The plugin solves two problems:
Good things is, this is a zero-setup plugin — and takes care of the above two point after simple activation. However, it drops in two plugins, and by default activates “nginx Compatibility (PHP4)”. Since, we’re using PHP5, deactivate that and active “nginx Compatibility (PHP5)” as you can see in the following screenshot:

Make sure to activate the currect nginx Compatibility plugin
Finally, access your W3 Total Cache settings… and you should be greeted by a lot of red warnings. Ignore them and scroll down to the bottom of the page.
W3 Total Cache automatically detects that the server is Nginx, and since it can’t have the easy way around of writing configs to .htaccess anymore, it will present you with a box to fill in the location of “Nginx server configuration file path”. Since we defined the location as /etc/nginx/w3-total.confin the vhost file, we enter the same full path here — as you can see in the screenshot below.

Enter the full path for W3 Total Cache config file here
Save settings.
Scroll to the top and click “auto-install” buttons on all those red warning boxes. Unfortunately, I don’t have a screenshot handy for that — but it basically implies that the specified settings are not available in the text file for it to refer to. (You don’t lose any of this plugin-specific custom settings because it also has the settings saved in the DB, and that’s why the prompt — the plugin is smart!) Clicking auto-install simply dumps the details, which it otherwise saves on a .htaccess in Apache, to our /etc/nginx/w3-total.conf location.
The best part: if you’re coming from a server environment where APC was not available, make W3 Total Cache’s opcode and db cache dropdowns to APC :-)
Go back to VPS SSH terminal, open /etc/php5/conf.d/apc.ini, and append the following settings (taken as it is from the tutorial I referred at the beginning of the article):
; configuration for php apc module extension = apc.so apc.enabled = 1 apc.shm_segments = 1 apc.shm_size = 512M apc.optimization = 0 apc.num_files_hint = 2700 apc.user_entries_hint = 2700 apc.ttl = 7200 apc.user_ttl = 3600 apc.gc_ttl = 600 apc.cache_by_default = 1 apc.slam_defense = 1 apc.use_request_time = 1 apc.mmap_file_mask = /dev/zero apc.file_update_protection = 2 apc.enable_cli = 0 apc.max_file_size = 2M apc.stat = 1 apc.write_lock = 1 apc.report_autofilter = 0 apc.include_once_override = 0 apc.rfc1867 = 0 apc.rfc1867_prefix = "upload_" apc.rfc1867_name = "APC_UPLOAD_PROGRESS" apc.rfc1867_freq = 0 apc.localcache = 1 apc.localcache.size = 1350 apc.coredump_unmap = 0 apc.stat_ctime = 0Reload the php5-fpm service
# /etc/init.d/php5-fpm reloadHopefully, you shouldn’t encounter any errors. Go back to your browser — to the settings page of W3 Total Cache and clear all cache.
View your site! We’re done here!
Finally, open your CDN administrator settings and enter the IP address of your VPS — I had to since we use MaxCDN as an Origin Pull CDN. And then go to your domain’s DNS server admin area and enter the new IP — since linuxforu.com is propagated by CloudFlare it took less than 15 minutes before I could remove the /etc/hosts file hack from my local system and was accessing the website from the new VPS — pages served by Nginx :-)
BTW, here’s some benchmark test (using the ab utility that also comes as part of the apache2-utils package that we’d installed for htpassed utility earlier) — for 500 simultaneous connections 10,000 times:
$ ab -n 10000 -c 500 http://www.linuxforu.com/ This is ApacheBench, Version 2.3 <$Revision: 655654 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking www.linuxforu.com (be patient) Completed 1000 requests Completed 2000 requests Completed 3000 requests Completed 4000 requests Completed 5000 requests Completed 6000 requests Completed 7000 requests Completed 8000 requests Completed 9000 requests Completed 10000 requests Finished 10000 requests Server Software: nginx Server Hostname: www.linuxforu.com Server Port: 80 Document Path: / Document Length: 75493 bytes Concurrency Level: 500 Time taken for tests: 3.086 seconds Complete requests: 10000 Failed requests: 0 Write errors: 0 Total transferred: 758990000 bytes HTML transferred: 754930000 bytes Requests per second: 3240.81 [#/sec] (mean) Time per request: 154.282 [ms] (mean) Time per request: 0.309 [ms] (mean, across all concurrent requests) Transfer rate: 240209.24 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 1 5.6 0 30 Processing: 28 43 15.3 39 268 Waiting: 20 42 14.9 39 268 Total: 35 44 18.3 40 268 Percentage of the requests served within a certain time (ms) 50% 40 66% 40 75% 40 80% 41 90% 41 95% 69 98% 122 99% 131 100% 268 (longest request)That’s quite impressive on a standard 2GB VPS with one cpu core :-) Try to replicate the same results on Apache.
Dealing with logsOne final step is set correct server paths so that logroate can also compress the logs for our Nginx “access” and “error” logs located at the non-standard location /srv/www/linuxforu.com/logs/. Open /etc/logrotate.d/nginx, and make sure it reads like this:
/srv/www/linuxforu.com/logs/*.log { daily missingok rotate 52 compress delaycompress notifempty create 0640 www-data adm sharedscripts prerotate if [ -d /etc/logrotate.d/httpd-prerotate ]; then \ run-parts /etc/logrotate.d/httpd-prerotate; \ fi; \ endscript postrotate [ ! -f /var/run/nginx.pid ] || kill -USR1 `cat /var/run/nginx.pid` endscript }That’s all folks!Related Posts:
Recent comments
43 weeks 3 days ago