Hosting multiple WordPress websites with 1 set of code?


#1

I have a number of different client websites on the same server. They are totally separate clients who do not know each other and thus would NEVER share users (i.e. no wordpress multisite possible).

Right now if I have 20 sites on the server, I have 20 copies of WordPress, and 20 copies of each theme and plugin. I choose which plugins and themes the clients are allowed to have.

I am running nginx + apc + memcached + fastcgi as outlined on the rtcamp site here and it works good.

Question: I want to put all of my WordPress core files, themes and plugins in the server root dirctory ~/wordpress and have all the sites simply reference those files.

Each site of course has its own wp-config.php file, and it's own wp-content/uploads folder for their content.

HOW DO I DO THIS?

I've kind of seen a few articles on putting WordPress into a subdirectory and then using .htaccess rules to make it kind of work, but I have seen nothing after days of looking on how to do this right with nginx.

As many WordPress hosting companies that there are out there, I've got to guess that someone somewhere has written a really good how-to set this up article... any ideas?... I'm stuck

*some of the sites would be single sites, some would be multisite, some would be multisite-subdomains, I don't think that stops the ability to use the same base code, themes and plugins though.


#2

Ok, got it working. I’m not sure if this is the best way, or if there are pitfalls to doing it this way that I will run into in the future, but for now it works.

The premise was to have a single install of WordPress core, themes, and plugins, in one folder that is accessed by all sites on the server. Each site would have its own database and uploads folder.

Here is what I did.

  1. create a folder to hold WordPress and the themes and plugins
    /home/wordpress

  2. create a folder that holds my websites.
    /home/wordpress-sites
    Each site has its own folder using the sitename (example.com) as the folder name. Each folder contains a db-config.php file and an uploads folder to store the sites uploads

  3. I edit the wp-config.php global file and remove the database connection parameters and the database prefix from the file.

I replace that information with the following:

$website = strtolower( str_replace( “www.”, “”, $_SERVER[“SERVER_NAME”] ) );
$website = preg_replace(’[^a-z0-9.-]’, ‘’, $website );

if ( file_exists( dirname(FILE) . “/…/wordpress-sites/$website/db-config.php” ) ) {
require_once( dirname(FILE) . “/…/wordpress-sites/$website/db-config.php” );
} else {
echo “Sorry, no database configuration defined.”;
die;
}


Inside the db-config.php file for each site I basically add in the database connection parameters and the database prefix.
  
define( 'DB_NAME', 'dbname' );  
define( 'DB_HOST', 'localhost' );  
define( 'DB_PASSWORD', 'password' );  
define( 'DB_USER', 'user' );  
$table_prefix  = 'wp_';  

That allows each site to have its own database

I then add in a custom uploads folder definition


/* Change uploads path */
define(‘UPLOADS’, ‘domains/example.com/files’);

Now there is a problem with this in that the UPLOADS path CANNOT be an absolute path, it has to be a relative path which sucks and I have no idea why they do this but there is a way around it.

Inside the /home/wordpress folder I create a symlink to a folder I call domains and link that inside the /home/wordpress-websites/ folder.

I do this so that I can still use a relative path when defining the UPLOADS folder.

Finally I add in a ‘files’ folder inside the /home/wordpress-websites/example.com/ directory. I have to do this for each site, so that each site has its own folder to store uploads.

I don’t think this is the most elegant way of doing it but it works for me so far.

My whole goal in doing this was to make it so that the caching plugins wouldn’t have to cache 50 copies of WordPress, and 50 copies of all the themes and plugins on the server. I’m guessing it takes about 40-50MBs of cache memory for each site on the server. Multiply that by 50 sites and the ram resources add up fast. This way I am hoping that each cache plugin sees all the files as the same, thus only having to cache one copy.

People much smarter than myself when it comes to caching would have to answer that question. I truly don’t know the answer yet, but will reply back when I do with the results.

Currently there is not a guide anywhere on the net that I could find that really talks about properly setting up a single codebase for multiple WordPress sites. I am hoping that this info will lead to a great article by the real WordPress experts so that we can all learn more.


#3

Thanks for posting about findings. :slight_smile:

I will not take this path as many wordpress plugins are not coded perfectly. They may run into issues!

If sites are small, I will rather use wordpress-multisite. If sites are big and/or complex, then spending 10 MB cache memory won’t be a bigger concern.

WordPress-core files op-code cache won’t take much space in RAM. Object-cache will need to be separate anyway.

Have you considered using a company like - http://www.ovh.com/us/dedicated-servers/sp2.xml ?

They offer too much RAM & SSD. So personally, I will prefer buying more RAM than spending time in fixing poorly coded plugin.

In any case, if you manage to get your config up and running for long, please post about it somewhere. :slight_smile:


#4

I totally understand your point. My wp-config.php relevant changes are actually listed above already. I can say that so far I have installed about 100+ plugins and have yet to run into an issue. I think it is because of the way that I am running it. As far as any plugin is concerned it actually does look like a standard WordPress installation.

The one issue I have run into has to do with using opcode cache and redis. The problem is that with this setup I basically already have the cache systems activated before a website is added. This means that when I go to add a new website, I end up with a blank screen and errors in my logs. I actually have to remove the redis ‘index’ file and also the object-cache.php from the wp-content directory. At that point, I can then create the database and finish configuring a new install. Once that is done, I can then replace the redis ‘index’ file along with the object-cache.php file and everything works as normal.

Here is what I see in the nginx error logs when I visit what should be the WordPress install page of a new website on the server before I deactivate redis.

root@wphotline:/home/wordpress# vi /var/log/nginx/wptumble.com.error.log  
2013/08/07 06:39:21 [error] 2055#0: *36 FastCGI sent in stderr: "PHP message: PHP Notice:  Undefined index: HTTP_CACHE_CONTROL in /home/wordpress/index.php on line 59" while reading response header from upstream, client: 123.123.123.123, server: wptumble.com, request: "GET / HTTP/1.1", upstream: "fastcgi://unix:/var/run/php5-fpm.sock:", host: "mysite.com"  
2013/08/07 06:40:00 [error] 2055#0: *38 FastCGI sent in stderr: "PHP message: PHP Notice:  Undefined index: HTTP_CACHE_CONTROL in /home/wordpress/index.php on line 59  
PHP message: PHP Fatal error:  Uncaught exception 'Predis\ServerException' with message 'invalid expire time in SETEX' in /home/wordpress/predis.php:562  
Stack trace:  
#0 /home/wordpress/predis.php(689): Predis\ResponseErrorHandler->handle(Object(Predis\Connection), 'ERR invalid exp...')  
#1 /home/wordpress/predis.php(1374): Predis\ResponseReader->read(Object(Predis\Connection))  
#2 /home/wordpress/predis.php(1383): Predis\Connection->readResponse(Object(Predis\Commands\SetExpire))  
#3 /home/wordpress/predis.php(202): Predis\Connection->executeCommand(Object(Predis\Commands\SetExpire))  
#4 /home/wordpress/wp-content/object-cache.php(954): Predis\Client->__call('setex', Array)  
#5 /home/wordpress/wp-content/object-cache.php(954): Predis\Client->setex('wptb_default:is...', false, 0)  
#6 /home/wordpress/wp-content/object-cache.php(613): WP_Object_Cache->set('is_blog_install...', false, '', 0)  
#7 /home/wordpress/wp-includes/functions.php(1123): wp_cache_set('is_blog_install...', false)  
#8 /home/wordpress/wp-includes/" while reading response header from upstream, client: 24.16.92.32, server: wptumble.com, request: "GET / HTTP/1.1", upstream: "fastcgi://unix:/var/run/php5-fpm.sock:", host: "mysite.com"  

Anyhow, other than this one issue, which I will figure out, I have yet to have any problems. If and when I do run into issues, I will report them back so other people can benefit form the knowledge I gain.


#5

BTW, I should mention…

I already have my own dedicated colo servers with more than enough ram and hd storage for anything I would ever dream of building. I am basically doing this as a challenge to myself to see how far I can optimize a little $10/month cloud server. I do have to admit though that the pricing on that site is amazingly low


#6

I must say that luck is on your side!

I came across few plugins recently which expects wp-config.php to be in webroot only. As a security practice we put it outside webroot. WordPress never had any issues but some plugins broke on a client sites!

Anyway, what you are doing is certainly interesting. :-)

I am also exploring pre-caching techniques. Op-code caching isn't a concern since once PHP starts most of WordPress' php files opcode cache should be created on first run. I am interested in adding an option in nginx-helper to pre-cache all pages on a wordpress site (may be in redis data-store)


#7

I have come across a few plugins in the past that expect wp-config.php to be in the webroot, which of course is totally wrong. When that happens I point them to the codex and supply a patch. Every plugin to date that I find with the issue makes the change.

My approach to the single codebase is a bit unique. By not putting any database params in the file and using a dynamic directory to force include an include file which of course includes the db info it seems to work. In that file, I also use the uploads= statement to give a custom uploads directory. As far as any code or plugin is concerned it is a standard setup.

Like I said, the only issue I have is that I use the redis server which means that I have a custom index.php file and also have the object-cache.php file activated in the wp-content directory. This makes adding new sites very difficult as I have to deactivate the cache on all sites to get it to install.

An approach I am taking now is to actually use a completely separate install directory for the wordpress files that don't include the cache. So for install, I point to a 'virgin' directory of files, get it installed, and then simply switch over the wordpress path after the fact. I am going through the database to see if wordpress actually stores the path to the wordpress files during install or not. If it does, then I need to write a script to auto change that as well.

Eventually I will be able to make this into a 'server script' acting like a custom installer for people to use. That is the goal at least.

btw Redis works amazing, but to date I don't think there is a 'cache flush' system for flushing the cache on pages with new comments etc. I'm to new to all of this to know one way or the other, but learning fast.


#8