Migrating WordPress From Dreamhost LAMP To Nginx Web Server

Migrating WordPress from Dreamhost LAMP to Nginx Web Server

Migrating a WordPress site from a LAMP server to a LEMP server can be rather daunting even when you have quite a bit of documentation, but scattered, and some help, but limited, from Dreamhost. Here I will summarize how I got to migrate the initial WordPress site from my LAMP VPS to a LEMP VPS running Nginx.

Setting up new LEMP VPS

First you need to create a new VPS and pick NGINX as HTTP server. That can be done in the DH control panel and is done in 5-10 minutes. Then you need to create a new admin user to start managing this server. You can add a new admin from the cpanel as well. I assume you know your way around the backend. If not consult the Dreamhost Wiki for this.

PHP CGI Child Processes

The next step I did was adding the necessary PHP child processes as these are not spawned or loaded on the fly on Nginx. Add PHP child processes to $HOME/domain.com and adjust child processes in /dh/nginx/servers/httpd-ps123456/environ from 18 to desired number which is 8 according to the Dreamhost Nginx Wiki . According to Otto at WordPress StackExchange  6 will do and I will go for that for now.  Here is the content of the .php-launcher I added to the user I created for the WordPress site under /home/user .

exec /dh/cgi-system/php5.cgi $*

I had a hard time getting the permalinks up and running on my site and that was partly because I was too fast and partly because the documentation was not that clear. In the end I turned Cloudflare of which saved me time reloaded all after emptying cache. Could have turned off W3T Cache, but I just emptied cache for it and restarted Nginx every change I made. The best thing is to set this up well before you move the site to the new VPS running Nginx.


Based on WP’s Codex  I added a new wordpress.conf with the following data even though I removed some of the elements:

#WordPress single blog rules.
# Designed to be included in any server {} block.

# This order might seem weird - this is attempted to match last if rules below fail.
# http://wiki.nginx.org/HttpCoreModule
location / {
try_files $uri $uri/ /index.php?$args;

# Add trailing slash to */wp-admin requests.
rewrite /wp-admin$ $scheme://$host$uri/ permanent;

# Directives to send expires headers and turn off 404 error logging.
location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
access_log off; log_not_found off; expires max;

Then I restarted the server: /etc/init.d/nginx restart

Wrong wordpress.conf location

I realized I had to add the wordpress.conf file with permalink or rewrite rules in a different location:


I added them to the domain root first, but that is wrong. You need to create a directory nginx under your nginx user and there you add another domain for your domain and there you need to add wordpress.conf with all the rewrite rules and other rules. Once done you do another /etc/init.d/nginx restart as root and empty cache where not be

Now the permalinks should be purring like a kitten!

Migrating WordPress from VPS 1 to VPS 2 (NGINX)

In the Dreamhost Control Panel you can go to Domains and click on edit to edit one domain. Then you can select the new user on the second VPS where you have Nginx running

After a while you will see the following on your frontend homepage:

We apologize for the inconvenience. Please contact the webmaster/ tech support immediately to have them rectify this.

error id: "bad_httpd_conf"

Dreamhost wrote a page on this explaining it all.  So I did a:

dig +short domain.com

dig +short @ns1.dreamhost.com domain.com

and ip addresses were identical so the IP address was connected to the domain/DNS. But I still had that bad httpd.conf error and funnily enough the browser loaded a different ip address than Dig came up with it.

So I opened the domain in the Dreamhost Control Panel under edit mode. Realised I had not emptied Cloudflare cache so I did, but still had the same error. Only after a few loads Cloudflare loaded a snapshot and I did not get the error. But site was still down.

Nginx restart

Decided to restart Nginx to make sure all was running:

sudo /etc/init.d/nginx restart
[sudo] password for admin:
/etc/init.d/nginx: line 13: ulimit: open files: cannot modify limit: Invalid argument
Stopping httpd-psxxxxxx webserver... stopped.
Stopping php:  imagewize not running... done!
Starting httpd-psxxxxxx webserver... [alert]: Phusion Passenger is disabled
because the 'passenger_root' option is not set. Please set this option if you want to enable Phusion Passenger.
Starting php:  imagewize launched... ok, chowning sockets... imagewize:  OK done!

Checked if PHP was running:

[psxxxxxx]$ php -v
PHP 5.2.17 (cli) (built: Mar  1 2012 16:36:14)
Copyright (c) 1997-2010 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2010 Zend Technologies
with Zend Extension Manager v1.2.2, Copyright (c) 2003-2007, by Zend Technologies
with XCache v1.2.2, Copyright (c) 2005-2007, by mOo
with Zend Optimizer v3.3.9, Copyright (c) 1998-2009, by Zend Technologies

All good!

W3 Total Cache under NGINX

You can now fix your W3 Total Cache if you  migrated it too and had it running under Apache2 before.  Enter admin and go to W3 Total Cache. Page Caching was disabled and there was a request to add a nginx.conf to the site root with the following:

# BEGIN W3TC Page Cache core
rewrite ^(.*\/)?w3tc_rewrite_test$ $1?w3tc_rewrite_test=1 last;
set $w3tc_rewrite 1;
if ($request_method = POST) {
set $w3tc_rewrite 0;
if ($query_string != "") {
set $w3tc_rewrite 0;
if ($http_host != "www.imagewize.com") {
set $w3tc_rewrite 0;
set $w3tc_rewrite2 1;
if ($request_uri !~ \/$) {
set $w3tc_rewrite2 0;
if ($request_uri ~* "(sitemap(_index)?\.xml(\.gz)?|[a-z0-9_\-]+-sitemap([0-9]+)?\.xml(\.gz)?)") {
set $w3tc_rewrite2 1;
if ($w3tc_rewrite2 != 1) {
set $w3tc_rewrite 0;
set $w3tc_rewrite3 1;
if ($request_uri ~* "(\/wp-admin\/|\/xmlrpc.php|\/wp-(app|cron|login|register|mail)\.php|\/feed\/|wp-.*\.php|index\.php)") {
set $w3tc_rewrite3 0;
if ($request_uri ~* "(wp-comments-popup\.php|wp-links-opml\.php|wp-locations\.php)") {
set $w3tc_rewrite3 1;
if ($w3tc_rewrite3 != 1) {
set $w3tc_rewrite 0;
if ($http_cookie ~* "(comment_author|wp-postpass|wordpress_\[a-f0-9\]\+|wordpress_logged_in)") {
set $w3tc_rewrite 0;
if ($http_user_agent ~* "(W3\ Total\ Cache/0\.9\.2\.4)") {
set $w3tc_rewrite 0;
set $w3tc_ua "";
set $w3tc_ref "";
set $w3tc_ssl "";
set $w3tc_enc "";
if ($http_accept_encoding ~ gzip) {
set $w3tc_enc _gzip;
set $w3tc_ext "";
if (-f "$document_root/wp-content/w3tc/pgcache/$request_uri/_index$w3tc_ua$w3tc_ref$w3tc_ssl.html$w3tc_enc") {
set $w3tc_ext .html;
if ($w3tc_ext = "") {
set $w3tc_rewrite 0;
if ($w3tc_rewrite = 1) {
rewrite .* "/wp-content/w3tc/pgcache/$request_uri/_index$w3tc_ua$w3tc_ref$w3tc_ssl$w3tc_ext$w3tc_enc" last;
# END W3TC Page Cache core

I did and then on reloading the page I had another 404, not an nginx 404. Decided to restart Nginx using

/etc/init.d/nginx restart 

as root. Probably needed it to find that new configuration anyways.

Location W3T Cache config file under Dreamhost

After some more reading I realized Dreamhost recommends you to copy this into your


folder. This to load it all properly. So I did and restarted the VPS. Later on I realized a symlink as Otto mentioned here is much more effective. It all seems to be up and running now.

Further W3T Total Cache enhancements

I made the nginx.conf part of the user the site is installed under and did an auto-intall by W3T of page cache enhancement and browser caching. I used XCache opt Cache anywhere I could. XCache and Nginx seem to get along just fine.

FYI The only issue is that you need to empty cache in W3Total Cache, Cloudflare and restart the server every change you make. There were still outstanding issues and tweaking to be done though.

Expire Headers

Somehow Yslow kept on complaining I needed to set Expires Headers while I had added that to the Browser Cache Settings in W3T and I checked nginx.conf in the domain.com folder and they were set:

# BEGIN W3TC Browser Cache
gzip on;
gzip_types text/css application/x-javascript text/x-component text/richtext image/$
location ~ \.(css|js|htc)$ {
expires 31536000s;
add_header Pragma "public";
add_header Cache-Control "max-age=31536000, public, must-revalidate, proxy-rev$
add_header X-Powered-By "W3 Total Cache/";
location ~ \.(html|htm|rtf|rtx|svg|svgz|txt|xsd|xsl|xml)$ {
expires 3600s;
add_header Pragma "public";
add_header Cache-Control "max-age=3600, public, must-revalidate, proxy-revalid$
add_header X-Powered-By "W3 Total Cache/";
location ~ \.(asf|asx|wax|wmv|wmx|avi|bmp|class|divx|doc|docx|eot|exe|gif|gz|gzip|$
expires 31536000s;
add_header Pragma "public";
add_header Cache-Control "max-age=31536000, public, must-revalidate, proxy-rev$
add_header X-Powered-By "W3 Total Cache/";
# END W3TC Browser Cache

I read here  that it should be

location ~* \.(jpg|png|gif|jpeg|css|js|mp3|wav|swf|mov|doc|pdf|xls|ppt|docx|pptx|xlsx)$ {

expires 7.776.000;


for images. So perhaps I needed to add more. I think this was the correct thing to do, but I still had complaints about several files having expire headers without far-ahead expiration dates such as .css files, some with ?v postfixes and some js?v javaScript files. But also an .ico file as well as some .pngs .  In the end expires headers was added. Perhaps it was a caching thing. Now I get an C for Expires Headers when logged off. Still not that great a grade, but only because of expire header issues with files located on other servers like Google and so on. The average YSlow score is now 90. Not bad!

RAM Used

I am currently using 200-300 MB Ram and set the RAM Max at 350MB I will add 7is7 Perl VPS Manager script to have auto scaling.  I cannot say I can put this site at 200 MB Max yet. Perhaps with auto scaling activated, but my experience has so far been that I need to have a 100-200 MB minimum buffer zone. On Apache the script could not handle peaks with a minimum below that. But we shall see. If all goes well I will have to deal with less peaks on Nginx. And that would make the moval worthwhile altogether!

Featured Image: Linux Screenshots


Been working with WordPress, SEO, content marketing and the web for 12+ years. When I am not coding, reading about the web or dreaming the web of things I travel or run a few blocks

Leave a Reply

Your email address will not be published. Required fields are marked *