Ryan McDonnell - Pursuing Web Application Zen

Multiple blogs, one WordPress install, zero code changes

Thursday, July 12th, 2007 at 6:00 am

Now I know there are plenty of patches and other projects out there to host multiple blogs from a single installation of WordPress. Many of them focus on trying to host multiple blogs that have a common element such as users, site design, or subdomains. What I wanted to do was a bit different.

  • One installation of WordPress to make future upgrades easy
  • Run multiple distinct and completely unique sites from that single installation
  • Each site is configured as its own virtual host within Apache
  • Each site has a unique design, theme, database, users, etc.
  • There should be zero changes to the code file included with WordPress.
  • Future version upgrades should be as easy as uploading the newest files.
  • Separate ‘wp-config.php’ configuration file for each blog/site.
  • Everything about each blog/site is under one directory that I can provide different users access to via FTP.

Installing Wordpress

First things first, I downloaded the latest version of WordPress, unzipped it and uploaded it via FTP to my server. I chose to to upload it to “/var/www/.wordpress” and gave it the appropriate permissions.

Open ‘wp-config.php’ (create it if it doesn’t exist) and insert the following into it:

<?php require('local/wp-config.php'); ?>

That’s it. Your single installation is now ready to serve multiple sites.

Setup a new blog

Now comes the fun part. Create the folder you’ll see running the new blog under. For this example, I’ll use ‘/var/www/example.com’. This is the folder that will serve as the root folder for the virtual host under Apache.

Under the newly created folder, create an ‘.htaccess’ file and insert the following line into it:

php_value include_path ".:/var/www/example.com/local/:/var/www/example.com/"

Now create a new folder called “local”. Within that folder, put the ‘wp-config.php’ file that you would normally use with WordPress.

<?php
define('DB_NAME', 'wp_example');
define('DB_USER', 'example');
define('DB_PASSWORD', 'password');
define('DB_HOST', 'localhost');
prefix $table_prefix  = 'wp_';
define('WPLANG', '');
define('ABSPATH', '/var/www/example.com/');
require_once(ABSPATH.'wp-settings.php');
?>

There is one key change you need to make. Edit the second to last line and define the ‘ABSPATH’ variable to be an absolute path to the root of this site.

Now, drop back into the root folder of the site. Create a file called ‘wp-config.php’ and put the following line in it:

<?php require('local/wp-config.php'); ?>

So now your file structure should look like this so far:

/www/var/example.com/.htaccess
/www/var/example.com/wp-config.php
/www/var/example.com/local/wp-config.php

We’re almost there. Now create symbolic links to all the actual WordPress files/folders except for the ‘wp-content’ folder.

index.php -> /var/www/.wordpress/index.php
wp-admin -> /var/www/.wordpress/wp-admin/
wp-atom.php -> /var/www/.wordpress/wp-atom.php
wp-blog-header.php -> /var/www/.wordpress/wp-blog-header.php
wp-comments-post.php -> /var/www/.wordpress/wp-comments-post.php
wp-commentsrss2.php -> /var/www/.wordpress/wp-commentsrss2.php
wp-cron.php -> /var/www/.wordpress/wp-cron.php
wp-feed.php -> /var/www/.wordpress/wp-feed.php
wp-includes -> /var/www/.wordpress/wp-includes/
wp-links-opml.php -> /var/www/.wordpress/wp-links-opml.php
wp-login.php -> /var/www/.wordpress/wp-login.php
wp-mail.php -> /var/www/.wordpress/wp-mail.php
wp-pass.php -> /var/www/.wordpress/wp-pass.php
wp-rdf.php -> /var/www/.wordpress/wp-rdf.php
wp-register.php -> /var/www/.wordpress/wp-register.php
wp-rss.php -> /var/www/.wordpress/wp-rss.php
wp-rss2.php -> /var/www/.wordpress/wp-rss2.php
wp-settings.php -> /var/www/.wordpress/wp-settings.php
wp-trackback.php -> /var/www/.wordpress/wp-trackback.php
xmlrpc.php -> /var/www/.wordpress/xmlrpc.php

I didn’t want to make symbolic link to the ‘wp-content’ folder because I wanted to be able to edit the themes individually for each site. To get that working, create a folder called ‘wp-content’ in your site (‘/var/www/example.com/wp-content’). Under ‘wp-content’, create a ‘themes’ folder and then make the following symbolic links:

/var/www/example.com/wp-content/index.php -> /var/www/.wordpress/wp-content/index.php
/var/www/example.com/wp-content/plugins -> /var/www/.wordpress/wp-content/plugins/
/var/www/example.com/wp-content/themes/default -> /var/www/.wordpress/wp-content/themes/default

The last symbolic link will make the default theme available to you until you upload a new one.

Everything is go!

Proceed with your WordPress installation like normal (database setup, login into admin area, etc).

Plugins

The ‘plugins’ folder has a central location under ‘/var/www/.wordpress/wp-content/plugins’. Upload your plugins there and all your blog can benefit from them. Each blog can enabled to use only the plugins you want to use.

Future versions

When future WordPress versions are released, just upload them to the ‘.wordpress’ folder and you’ve updated all your blogs at once! With each new version, take a quick look at the files included incase they’ve added some new files which you’ll need to create symbolic links for.

Caveats

Unfortunately my method requires some command-like access via a terminal/SSH and this method cannot be used with most shared hosting providers. Perhaps someone can build a small script that builds the symbolic links?

Some directives in the ‘.htaccess’ may not work unless you define ‘AllowOverrides All’ in the Apache virtual host configuration.

How it works

Symbolic links can cause problems when using the include() or require() functions within PHP. The formatting of the requested file’s path can influence if the file is included from the symbolic link’s folder or the original folder that the site is running under.

My solution to that problem is contained within the ‘.htaccess’ file. The PHP engine is configured to look for include files within a path before it the current folder is checked. This allows us to override the location of the ‘wp-config.php’ file and always grab the copy located within the ‘local’ folder on each site.

If you have any questions or issues please leave a comment below!

71 Comments

  1. Glenn

    Nice approach Ryan. How do you manage de-activating plugins for an upgrade? Does renaming your master plugins directory – temporarily for the upgrade – have the same effect as disabling the plugins for each blog?

  2. Ryan McDonnell

    Thanks for the comment!

    I’ve not tested that yet as I personally don’t deactivate plugins when performing an upgrade.

    I believe you are correct in that renaming the folder would disable the plugins. I know that when a plugin is being unruly and you can’t even access the admin area, that deleting the plugin solves the problem. I’ll test your theory out during the next WordPress upgrade.

  3. Tamlyn Rhodes

    Good stuff. I was thinking of using a similar approach for a site I’m working on. Glad to see someone’s got it working with a minimum of fuss.

  4. Qrystal

    This is exactly what I was hoping would be easily done, but I decided to search around to see if anyone’s tried it (or put its setup into a script…)

    Any updates on this method? Did you upgrade to Wordpress 2.3.2?

  5. Ryan McDonnell

    I currently don’t use this method anymore so I have not tested how well it works with the more recent versions of Wordpress. I don’t see any obvious reasons it would not work though.

    If you happen to try it, please follow up and let me know how it goes. :-)

  6. Gary LaPointe

    Ryan,

    I’m trying to make sure understand why I wouldn’t just make dynamic links to everything (except the wp-config file)?

    The local folder part is confusing, but I think I need to understand it before I commit to this :)

    Is the htaccess mod only for the include() / require() functions or does it help other things too? (I think it’s just for the functions).

    Lastly, why aren’t you using this method anymore? what are you using?

    Thanks
    Gary

  7. Ryan McDonnell

    @Gary:

    What do you mean by dynamic links?

    The local folder part is a way to work around how PHP interprets the path when using the “include” and “required” directives. It also serves to create a folder to be the first target of the “incudes” path. There may be a more elegant way to achieve this solution but the above worked well for my needs.

    I’m not using this method any more simply because I don’t have multiple WordPress blogs running on the same server anymore. If I did, I think I’d still use this method.

  8. Gary LaPointe

    Sorry, I meant symbolic links (I don’t know where that came from).

    I think I’m just going to have to trust you on the local folder part.

    Thanks very much for the info. A few other people have done similar posts, but not as straightforward (they completely muck with the .htaccess file, where the symbolic links seem better).

    I think I’ll be trying this soon (maybe today)

  9. Ryan McDonnell

    When the WordPress code requests to include the “wp-config.php” file, PHP would look for “wp-config.php” relative to the local path. Since the WordPress code is in a common location (/var/www/.wordpress) it won’t be able to find the “wp-config.php” that pertains to the current blog. Instead, I’ve mapped the “wp-config.php” to a folder called “local” and included that path in the “includes” search path. Now when the WordPress code requests to include the “local/wp-config.php” file, PHP searches in the “includes” path and is able to locate the correct “wp-config.php” file for the active blog.

    Hopefully that makes more sense.

  10. Gary LaPointe

    Ahh… I’m understanding the local folder part better. When I was reading it I didn’t realize there were TWO wp-config.php files, as I was following the steps and saw the second it made a lot more sense (and the “php_value include_path” looks useful for future projects). I’d say I understand it now, except…

    I’m really close but it’s still not working. (I think it’s the “php_value include_path” stuff, or possibly permissions)

    My test site is /mypath/_sites/gaea.com
    My wordpress files are at /mypath/_sites/_multipress/wordpress-current
    my .htaccess line says
    php_value include_path “.:/mypath/_sites/gaea.com/local/:/mypath/_sites/gaea.com/”

    Unfortunately, I get a “500 Internal Server Error”. But I think I’m really close.

    I can get around it by hardcoding the full path in both wp-config.php files and remove the line from the .htaccess then it lets me get the “name my blog” config page. But obviously this isn’t going to work for more than one site. (Even hard coded it won’t work until I remove the .htaccess file)

    If I remove the .htaccess line and have the config.php lines they way you say I get
    Warning: main(local/wp-config.php) [function.main]: failed to open stream: No such file or directory in /home/garyforp/public_html/_sites/_multipress/wordpress-current/wp-config.php on line 1
    Warning: main(local/wp-config.php) [function.main]: failed to open stream: No such file or directory in /home/garyforp/public_html/_sites/_multipress/wordpress-current/wp-config.php on line 1
    Fatal error: main() [function.require]: Failed opening required ‘local/wp-config.php’ (include_path=’.:/usr/lib/php:/usr/local/lib/php’) in /mypath/_sites/_multipress/wordpress-current/wp-config.php on line 1

    otherwise I get the 500 error. So it appears the .htaccess line is doing something…

    Any thoughts? I’d love for this to work and I think I’m very close…

    Gary

  11. Ryan McDonnell

    I think your problem lies with the directive in the .htaccess file. If you are in shared hosting environment, you may not have permission to use “php_value”. Your host may provide the ability to edit a unique php.ini file for your site, and you can change the PHP include_path variable in there.

    If it’s not a shared hosting environment, check the Apache configuration for the site. See the “Caveats” subheading in the article regarding “AllowOverrides All”.

  12. Gary LaPointe

    I figured if you didn’t spot something specific, it’d be something along those lines. I will check in and let you know what I find…

    Thank you SO much for your time Ryan.
    Gary

  13. Gary LaPointe

    Unfortunately, it appears Site5 won’t let me put it in the .htaccess

    I’m running php4 (by default), so I’m trying some local PHP.INI files. It seems like the correct spot to put them is in the site directory and the wordpress directory which both have the wp-config files with the “local” path (it’s possible I need them in every subdirectory) but I can’t even get them to work with static paths (unless I need every subdirectory). I’ll play with this a bit longer.

    I can’t change the php.ini for my server either. But unless I can just add “local” to the search path, a specific path wounldn’t work for me anyway, right? (can I add just local)

    The same goes for php5 which lets me have a master local php.ini file but since I need a specific path for each fake WP install, I’m still out of luck.

    If I give up at Site5, I’ll try another server I have access to.

    If that doesn’t work, I’ll try the way some others were using one install (for multiple sites) by parsing the domain name (in htaccess?) and modifying some other code (mostly in WP config I think). I wasn’t too keen on that method. The way you were doing it was the way I thought would be best.

  14. Ryan McDonnell

    I figured. It’s common for shared hosts to not provide such permissions.

    From my experience, the include path was required to be able to dynamically “include” the proper wp-config.php file for each site.

    My goal in the above method was to completely avoid editing any WordPress code. If you are willing to forego that requirement, I suggest you look into changing the PHP include path using the ini_set method in PHP. You may be able to change that setting via PHP code within the wp-settings.php file or another section of code that is common to every page.

  15. Simon Thompson

    Thanks for the writeup on this, it’s been super helpful. I’ve just managed to get this running in the last couple of days, and I’ve found that I didn’t need the ‘local’ folder. I left the wp-config.php file in the directory of the domain, then told the wp-config in .wordpress/ to , it finds it fine.

    One thing that took me a while to figure out though is that if you’re in an environment with a number of vhosts (we’re with MT, so I have root access to the server, which is handy), you’ll need to add/edit the vhost.conf file for each domain to add

    php_admin_value open_basedir "/var/www/path/to/your/httpdocs:/tmp:/var/www/.wordpress"


    Then restart apache.

    I’m not sure if every server would need the /tmp dir, but MT does. Also, after I created the vhost.conf file, I had to register it with plesk:
    /usr/local/psa/admin/sbin/websrvmng --reconfigure-vhost --vhost-name=domain.com, then restart apache.

    Hopefully this also helps somebody else.

  16. Simon Thompson

    Hmm, previous comment missed a code block. The third sentence should read: I left the wp-config.php file in the directory of the domain, then told the wp-config in ./wordpress to
    require('./wp-config.php');
    And it finds the localised version fine

  17. Ryan McDonnell

    Glad to hear this worked for you Simon. Thanks for the follow-up.

  18. Simon Thompson

    A couple of other things I’ve found:
    As mentioned above, I don’t need the ‘local’ directory at all. Also, I don’t have to edit the wp-config.php file in the domain. Leaving it in the domain directory allows you to use:
    define('ABSPATH', dirname(__FILE__).'/');
    just fine. My copy of wp-config.php in the /.wordpress directory is able to find the domain version with its require call.

    @Gary: do you have FollowSymLinks turned on for your server? Try adding
    Options +FollowSymLinks
    to the start of your .htaccess file. It may be that your server has it turned off by default. I noticed some other people complaining of http 500 errors on other message boards when it was off.

  19. Ryan McDonnell

    @Simon: Which version of Apache and PHP are you using? Some of the differences in our solutions may be due to version differences. Good point about FollowSymLinks.

  20. Simon Thompson

    Hmm, good point. I’m on Apache 2.0.52 and php4.

    That’s something I’ll have to watch for when we switch to MT’s new server with php5 on it

  21. Simon Thompson

    Actually, scratch everything I said previously about not needing the ‘local’ folder :)

    If you try and get to wp-admin without it, it gets all confused.

  22. Ryan McDonnell

    Haha. :-)

    it’s been a while since I implemented this but that did jog my memory of running into issues with wp-admin and developing the need for the “local” folder and the PHP include path.

  23. Leonardo Parada

    Hello: Congratulations for your solution.
    I am testing some solutions for similar problem:
    I want have 5 blogs, with diferent themes.
    one data base
    one worpdress instalation
    one panel admistration.
    I don’t want use wordpressMU because I think that is very big for 5 blogs.
    This is other site with solutions, about that.
    http://striderweb.com/nerdaphernalia/features/virtual-multiblog/
    but I can’t found the right answer.
    …Will search more.
    Greetings.
    Leonardo Parada
    http://www.leonardoparada.cl

  24. [...] Multiple blogs, one WordPress install, zero code changes Digg it [...]

  25. Daniel Hernandez

    Really nice solution. If I only would have knew it earlier. Will try it with WP 2.5

  26. Optonotes

    Thanks for this solution, but I had better luck with this one. Very straightforward and plastic. It doesn’t require you do any crazy stuff with symbolic links, or a .htaccess — no need for terminal/SSH.

  27. Ryan McDonnell

    Thanks for the link Optonotes. That is a much simpler options but would combine all the assets for all the blogs into a single directory. The traffic logs would all be contained in the same log file which would make any server-side analytics useless.

  28. Martijn

    Just a quick note about the solution Optonotes added (mine).You *do* have the themes and plugins combined in one folder, but often that doesn’t hurt. As for the assets you can set ‘Store uploads in this folder’ to different paths for each blog. For analytics I would rather use Mint or Google Analytics than server side logs anyway.

  29. Ryan McDonnell

    Thanks for stopping by Martijn. Your solution is perfect for the user on a shared hosting environment or lacking SSH/terminal access.

  30. Ryan S.

    Ryan,

    I tried your solution and it worked for a little bit. Then my host changed my server obviously changing my php settings. You mentioned that you are no longer using this method. Are you implementing another method that is working for you and if so what is it. Can you link to it?

  31. Ryan McDonnell

    The problem this solves just no longer exists for me. I was using the solution to manage several blogs on the same server but since then all the sites have migrated to different platforms.

  32. [...] There are a few WordPress-centric solitions – WordPress MU, hacking WordPress so that you can run a bunch of WP sites off one install, and then there’s just installing each one separately. Do it manually at first (or via [...]

  33. [...] have five blogs deployed from a single instance of wordpress based on the instructions provided at Ryan McDonell (which unfortunately seems to have gone away. Arrrgh!) OK, well the gist of it is that you symlink [...]

  34. Melody

    Im having a really hard time, just at the symbolic link part. I dont understand where you put these links, and if they are in a file or if they are each a file.

    Im having a really hard time with multiple blogs. Your blog is by far the most easy to understand, most sites dont explain ANYTHING. you have to REALLY know what you are doing before trying this.
    Yours is great though, I just got stuck and dont know how to format the symbolic links. Thanks!

  35. Ryan McDonnell

    Symbolic links are an alias to another file. You’ll need access the command line via a shell/SSH to create them. Regular FTP won’t cut it.

  36. Brandi Boyd

    Hi Ryan. Great solution. I have it working with three blogs, but I’m having issues with the themes.
    Putting a theme in each individual site’s theme folder does not cause the theme to show up; and even without ANY symlink to the main wordpress installation’s theme folder, all themes in that folder show up for all sites, while themes in the site’s folder don’t appear.
    Example:
    the wordpress install is @ /home/presses/
    site1 is @ /home/site1/public_html/
    site2 is @ /home/site2/public_html/
    site3 is @ /home/site3/public_html/

    When I put the default and classic themes in the wordpress install @ /home/presses/wp-content/themes/
    and put one symlink to ‘default’ in all three sites, both themes show up in all three dashboards.
    When I put NO symlink in the /home/site#/public_html/wp-content/themes/ folder, both themes in the main install show up in the dashboard anyway.
    When I put a theme in the /home/site#/public_html/wp-content/themes/ folder, it does not appear, but the two from the wordpress install’s theme folder do, with or without symlinks.
    When I delete the theme folder in /home/presses/wp-content/themes/ and put default and site themes in the home/site#/wp-content/themes/ folder I get the following errors and no themes at all displayed:

    Warning: array_keys() [function.array-keys]: The first argument should be an array in /home/presses/wp-includes/theme.php on line 315

    The active theme is broken. Reverting to the default theme.

    Warning: ksort() expects parameter 1 to be array, boolean given in /home/presses/wp-admin/themes.php on line 33

    Warning: array_slice() expects parameter 1 to be array, boolean given in /home/presses/wp-admin/themes.php on line 52

    Ideally, I’d like to be able to maintain separate theme folders for each blog as stated in your brief, although I can live without it, it was my main reason for going with this method, which is, regardless, brilliant.

    Any ideas? Maybe some .htaccess trick to require the local path in the main theme folder?

  37. Ryan McDonnell

    @Brandi: Which version of WordPress are you using?

  38. Brandi Boyd

    2.6

  39. Brandi Boyd

    Happy to let you test on my setup if you’re not running 2.6 yet, just email me. I know it’s early yet and there were some core changes to the authentication and admin and config. Thought I’d give it a try. FYI to other readers, this method also works with existing sites very well.

  40. [...] VERY cool thing I did just implement is Ryan McDonnell’s method for running multiple (virtual host) blogs with one wordpress install and zero code changes. [...]

  41. Ryan McDonnell

    I’ll have to revive this method on my server for testing and see if 2.6 changed something related to find the themes path.

  42. Brandi Boyd

    No problem, still love the method. I will poke around too and let you know if I find something.

  43. James

    I’m having the same issue with 2.6. The constant WP_CONTENT_DIR is being set to the wordpress installation’s wp-content/ directory and not the local site.

  44. Mary

    Hi everyone,

    I have a solution for WordPress 2.6, tested on WP 2.6.2.

    The secret is to add two more lines after your custom ABSPATH definition in local/wp-config.php:

    define(‘ABSPATH’, /var/www/example.com/’);
    define(‘WP_CONTENT_DIR’, ‘/var/www/example.com/wp-content’);
    define(‘PLUGINDIR’, ‘/var/www/example.com/wp-content/plugins’);

  45. Mary

    The last variable, by the way, does mean that you don’t necessarily need a symlink for your plugins at all and could do this for every site:

    define (’PLUGINDIR’, ‘/var/www/.wordpress/wp-content/plugins’);

  46. Mary

    Final update I hope. The plugin variable needs to be WP_PLUGIN_DIR, not PLUGINDIR.

    eg,

    define (’WP_PLUGIN_DIR’, ‘/var/www/.wordpress/wp-content/plugins’);

  47. Rob Scott

    If you have root access, why not change the httpd.conf file? .htaccess increases server load hugely in comparison as I understand it. httpd.conf is loaded with apache.

  48. Ryan McDonnell

    You’ll need an .htaccess file for the WordPress URL rewrites anyhow so I doubt one extra line would impact performance considerably.

  49. ????????wordpress

    [...] ??????????????? [...]

  50. PerGu

    #!/bin/bash
    #
    # bash script for the work describe at
    # http://www.ryanmcdonnell.com/multiple-blogs-one-wordpress-install-zero-code-changes/
    # by pergu
    #
    # Put your variables here
    #
    # Path to webdirectory
    WEB=/var/www

    # Path to wordpress
    WORDPRESS=$WEB/wordpress

    # Blog URL
    PRE=”blog.”
    HOST=”example.com”

    # apache2 config file
    # 291 is that I use numbers in which order the config files should be read…
    APACHE=”/etc/apache2/sites-enabled/291-blog-$DIR”

    #### Do Not edit …
    echo -n “blog directory under $WEB : ”
    read -e DIR

    echo “URL http://$PRE$DIR$HOST

    WWW=$WEB/$DIR
    LOC=$WWW/local
    mkdir $WWW

    echo php_value include_path “.:$WWW/local/:$WWW/” > $WWW/.htaccess

    echo “”> $WORDPRESS/wp-config.php

    mkdir $LOC
    WPC=$LOC/wp-config.php
    echo ” $WPC
    echo “define(‘DB_NAME’, ‘wordpress-db-name’);”>> $WPC
    echo “define(‘DB_USER’, ‘wordpress-user-name’);”>> $WPC
    echo “define(‘DB_PASSWORD’, ‘wordpress-password’);”>> $WPC
    echo “define(‘DB_HOST’, ‘localhost’);”>> $WPC
    echo -n “\$table_prefix = ‘$DIR” >> $WPC
    echo “_’;”>> $WPCecho “define(‘WPLANG’, ”);”>> $WPC
    echo “define(‘ABSPATH’, ‘$WWW’);”>> $WPC
    echo “define (‘WP_PLUGIN_DIR’, ‘$WORDPRESS/wp-content/plugins’);” >> $WPC
    echo “define (‘WP_CONTENT_DIR’, ‘$WWW/wp-content’);” >> $WPC
    echo “require_once(ABSPATH.’wp-settings.php’);”>> $WPC
    echo “?>” >> $WPC

    echo Linking $WWW
    ln -s $WORDPRESS/index.php $WWW
    ln -s $WORDPRESS/wp-admin/ $WWW
    ln -s $WORDPRESS/wp-atom.php $WWW
    ln -s $WORDPRESS/wp-blog-header.php $WWW
    ln -s $WORDPRESS/wp-comments-post.php $WWW
    ln -s $WORDPRESS/wp-commentsrss2.php $WWW
    ln -s $WORDPRESS/wp-cron.php $WWW
    ln -s $WORDPRESS/var/www/wordpress/wp-feed.php $WWW
    ln -s $WORDPRESS/var/www/wordpress/wp-includes/ $WWW
    ln -s $WORDPRESS/wp-links-opml.php $WWW
    ln -s $WORDPRESS/wp-login.php $WWW
    ln -s $WORDPRESS/wp-mail.php $WWW
    ln -s $WORDPRESS/wp-pass.php $WWW
    ln -s $WORDPRESS/wp-rdf.php $WWW
    ln -s $WORDPRESS/wp-register.php $WWW
    ln -s $WORDPRESS/wp-rss.php $WWW
    ln -s $WORDPRESS/wp-rss2.php $WWW
    ln -s $WORDPRESS/wp-settings.php $WWW
    ln -s $WORDPRESS/wp-trackback.php $WWW
    ln -s $WORDPRESS/xmlrpc.php $WWW

    cp -rfu $WORDPRESS/wp-content $WWW
    rm -rf $WWW/wp-content/index.php
    rm -rf $WWW/wp-content/plugins
    rm -rf $WWW/wp-content/themes/default

    ln -s $WORDPRESS/wp-co:ntent/index.php $WWW/wp-content
    ln -s $WORDPRESS/wp-content/plugins/ $WWW/wp-content
    ln -s $WORDPRESS/wp-content/themes/default/ $WWW/wp-content/themes

    PRE=”blog.”
    HOST=”.gustavsson.org”
    APACHE=”/etc/apache2/sites-enabled/291-blog-$DIR”
    echo “” >> $APACHE
    echo ” ServerName $PRE$DIR$HOST” >> $APACHE
    echo ” #ServerAlias xxx.$HOST” >> $APACHE
    echo ” DocumentRoot $WWW” >> $APACHE
    echo ” CustomLog /var/log/apache2/access-blog-$DIR.log combined” >> $APACHEecho “” >> $APACHE

    # reload apache
    /etc/init.d/apache2 reload

  51. [...] # # bash script for the work describe at # http://www.ryanmcdonnell.com/multiple-blogs-one-wordpress-install-zero-code-changes # # Put your variables here [...]

  52. [...] is really best suited to a single install per instance. Although there are guides to hack multiple instances from a single core install, it gets messy quick and usually relies on a [...]

  53. Chuck

    This is a great method, thanks.

    One issue though — WordPress won’t be able to update the .htaccess file (like if you update the permalink structure). PHP resolves soft links to their target filesystem paths. So when wp-admin/admin.php is loaded, the path for that file is /var/www/.wordpress/wp-admin/admin.php. It requires ‘../wp-load.php’ which PHP finds via the current file’s path (includes involving relative paths ignore the include path). This means /var/www/.wordpress/wp-load.php is loaded, which is significant because this is where ABSPATH is defined.

    Still trying to think of a nice way out, that doesn’t involved hard linking every single file…

    Chuck

  54. Ryan McDonnell

    Well WordPress only needs the same block of rules in the htaccess regardless of which permalink structure you choose. If you just place the default rules into the .htaccess yourself, there won’t be a problem.

  55. Chuck

    True, but I had to see if I could get it to just work. :-)

    By the way, this will do the trick: Add a file ‘admin.php’ to the ‘local’ subdirectory. The contents should look like this:

    <?php
    define(‘ABSPATH’, dirname(dirname(__FILE__)) . ‘/’);
    require(‘../wp-admin/admin.php’);

    Chuck

  56. Camilo

    Interesting solution. Thanks for sharing!

  57. [...] few small tweaks. If you also want to save maintenance and host more than one blog from one server, here is the article that I [...]

  58. Zenobius

    Hello,

    I’m interested what’s going to happen to the blogs when there are changes in database in wordpress update.

    Anyone had any problems with that?

  59. visdes

    does this method allow each blog to have its own unique, segregated RSS feed?

  60. Ryan McDonnell

    @visdes Yes, every blog is a unique installation.

  61. Ed Nailor

    Ryan,
    I have been considering doing this for quite some time and finally jumped in. It turned out to be much easier than I thought it would be, but I am running into one “glitch.”
    I have my install complete and am trying to upload an image to a post. I am getting an error when it tries to upload telling me it could not move the file to my MAIN install’s upload file. I want the uploads to also be independent from each other.
    I have set up folders in my wp-content file for uploads, cache, themes, gallery… the only symlink here is for the plugins as I do want them centralized. However, the system is seeing the upload file as the file on my main install, not the new install.

    Any ideas on how to correct?

    PS. I also discovered that using WP2.7 works well, but if you define the WP_CONTENT_DIR and WP_PLUGIN_DIR you get issues… by not defining them, those areas work just fine.

  62. Ed Nailor

    Further clarification:
    I have gone into the MAIN install and chmod’d the uploads file to 777 to allow uploads. Go back to my 2nd install and try to upload an image and now it uploads the file and stores it in the MAIN install, but the URL shown is the 2nd install’s URL. So for some reason the system is uploading to the MAIN install but still using the 2nd install’s URL structure.. either way, the image is inaccessible which is not what is desired. I even tried defining the UPLOADS url in the wp-config file, but that just got added to the path to MAIN install for some reason.
    Any ideas? How did you get around this?

  63. Ed Nailor

    The error is:
    The uploaded file could not be moved to
    /var/www/.wordpress/wp-content/uploads

  64. Ryan McDonnell

    @Ed

    Unfortunately I haven’t used this method in quote a while and that was well before 2.7 was released.

  65. reallife

    For anyone using dreamhost or similar PHP driven though CGI, be sure to check out this: http://wiki.dreamhost.com/PHP.ini for the include path as the .htaccess solution will not work (PHP is not loaded as Apache module, so it ignores the php_value commands).

  66. Zenobius

    Any idea about updating? ;-)

  67. Ryan McDonnell

    Since there are no changes made to the code base or the database, I have a hard time seeing how any database schema changes would be affected by this trick.

  68. Mads

    Hi!

    This patch (or what it’s called) is really great and helps me with my work :)
    Thank you SO much :)

    One little question that someone may be able to answer.
    Since I use this to host my customers sites, I want to have full admin access to all the blogs within the install of wp. Is there any way I can set some user-variables in the wp-config-file so that I grant admin-access to user/password on all the sites. It’s not a big deal since it’s easy to to manually, but it would be even greater :)

    Mads

  69. ByTheSea

    Hi Ryan,
    Found this post while looking for information on getting server side includes (“include it” plugin) functioning. (So far they aren’t). I’ve just recently started working with wordpress and have figured out how to have multiple blogs for one domain name. Not your system… nor wordpress mu… but some other way to do it that someone else figured out.
    (Basically copying the files, changing a line in the config file etc.)
    So everything does end up in the same database, and I do realize that doe have some risk… But OK for now and for this project.

    (And now that I know about these other approaches, might use them for future projects.)

    With the main part of the site ( done as a regular html), I have been using server side includes to handle the navigation section. Easy to update when needed.

    Now that I’m also adding these blogs, a different one for different new topics/themes, I want to incorporate a similar system of using server side includes that pull in that one navigation file thus allowing access across the entire website. (Existing html pages,+ new “theme/blogs”.)

    Having trouble pulling this off as the WP theme/blogs are not recognizing the navigation file. The code [include file=filename] inserted in the sidebar.php part of the blog theme actually ends up showing on the blog sidebar, instead of the actual file it is supposed to conjure up.

    Is there a way to get the include it plugin to work, for the situation described, without having to have a copy of the “nav” file uploaded to each and every blogette? That seems to defeat the point of using server side includes, as I would then have to update each and every “nav” file, upload etc. etc.

    Appreciate any insights you may have.

  70. [...] the wp-config file to include the real copy for the specific website in a specific location (see this). This solution seemed good, except having to create many symlinks, and if a future version of [...]

  71. Ryan McDonnell

    This concept/solution is over 2 year and several major releases old. I no longer use this method myself and many others have improved upon the solution (see trackbacks and links in comments above).

    Comments are now closed.

About Ryan McDonnell

I am a web application developer living in southern California. Commonly called a “jack of all trades” by collegues, I constantly strive to broaden my knowledge into other fields.
More about Ryan McDonnell »


 Subscribe in a reader

Quick Links & Notes

© 2004-2010 Ryan McDonnell. View my profile on LinkedIn
Some rights reserved under the Creative Commons Attribution-Share Alike 3.0 United States License.