Home / Migrating the Drupal way. Part III: managing multiple sites with Drupal

Migrating the Drupal way. Part III: managing multiple sites with Drupal

OK, you've got a few sites. Maybe you are a company with a handful of product sites, or maybe you are a university with hundreds of department sites. When working with more than one site you will inevitably run into problems with maintenance, revision control, deployment and the like. For my third installment on migrating to Drupal, I'd like to show you how using Multisite installations can make your life a whole lot easier.

Drupal Multisite

Drupal natively allows you to run multiple sites from a single installation. These sites can act as completely unique sites using independent databases, modules and themes. Or, with some configuration changes, the sites can share themes, modules and even database tables.

The obvious benefit is maintenance, and by extension you can easily keep your sites secure. When a new version of Drupal is released, you follow the normal (extremely short) upgrade path; but, it is only necessary to upgrade one site - you just have to run update.php on each of the unique sites.

Installing multiple sites is just as easy as installing your default Drupal site. You may either use multiple domain names (example1.com, example2.com) or you may use multiple subdirectories (example.com/subdirectory1, example.com/subdirectory2). If you are using multiple domain names, make sure that they all point to the same Drupal root directory on your web server (e.g. /var/www/html/yoursite).

Setting up your Drupal Multisite using multiple domain names

  1. Install Drupal
  2. Copy the "/sites/default" directory and its contents to "/sites/example.com"
  3. Create a new database
  4. Configure "/sites/example.com/settings.php"
  5. Edit your httpd.conf file to include a ServerAlias line for each domain or subdomain in your Multisite install so that all domains point to the same Drupal installation [Edited - Thanks Boris!]
  6. Navigate to example.com/install.php
  7. Repeat for as many domains as you like

Setting up your Drupal Multisite using multiple subdirectories

  1. Install Drupal
  2. Copy the "/sites/default" directory and its contents to "/sites/example.com.subdirectory"
  3. Create a new database
  4. Configure "/sites/example.com.subdirectory/settings.php"
  5. Create a symbolic link from inside your Drupal root that points to your Drupal root. (e.g. ln -s /var/www/example.com subdirectory)
  6. Navigate to example.com/subdirectory/install.php
  7. Repeat for as many subdirectories as you like

Then when it comes time to upgrade Drupal, you can follow the normal upgrade path. Just remember to run update.php on all of your sites!

This excerpt from settings.php helps to explain how Drupal locates the proper settings file:

* The configuration file to be loaded is based upon the rules below.
*
* The configuration directory will be discovered by stripping the
* website's hostname from left to right and pathname from right to
* left. The first configuration file found will be used and any
* others will be ignored. If no other configuration file is found
* then the default configuration file at 'sites/default' will be used.

Configuring settings.php

The options available in settings.php are fairly simple. A minimal configuration would simply involve updating the value of $db_url to use your new database. Take a minute to read through your settings.php file to see what other options are available.

Modules and Themes

The above methods involve adding directories to your sites directory. Adding themes and modules to sites/all/themes and sites/all/modules will provide them to each of your sites. If you wish to only allow one site to use a theme or module, you can create the directories: sites/example.com/themes and sites/example.com/modules. This will make the themes and modules in these directories exclusive to this site.

Taking Multisite a Step Further

The settings.php file is pretty simple to set up, but it also allows advanced users to set up some complex features. Let's say you want to have two sites that share users, but have unique content. It's worthwhile to note that you should have a good understanding of Drupal's tables and their relationships before attempting to use shared tables. Some tables depend upon others and need to be shared as a group to maintain data integrity.

So, if you aren't scared off by the warning, check out the following code from settings.php:

*   $db_prefix = array(
*     'default'   => 'main_',
*     'users'     => 'shared_',
*     'sessions'  => 'shared_',
*     'role'      => 'shared_',
*     'authmap'   => 'shared_',
*   );

The $db_prefix variable can be used to set a common database prefix, or you can specify prefixes at the table level. So, if you were to set up example1.com and example2.com to use the same database, you could use the following $db_prefix settings to share the users (and related tables) between the two:
// from /sites/example1.com/settings.php
$db_url = 'mysqli://username:password@localhost/databasename';
$db_prefix = array(
  'default'   => 'example1_',
  'users'     => 'shared_',
  'sessions'  => 'shared_',
  'role'      => 'shared_',
  'authmap'   => 'shared_',
);

Notice the same value for $db_url:
// from /sites/example2.com/settings.php
$db_url = 'mysqli://username:password@localhost/databasename';
$db_prefix = array(
  'default'   => 'example2_',
  'users'     => 'shared_',
  'sessions'  => 'shared_',
  'role'      => 'shared_',
  'authmap'   => 'shared_',
);

Here you can see that because of the 'default' setting one site will create tables like "example1_node", "example1_node_access", etc. while the other site will create "example2_node" and "example2_node_access". You can now use unique modules, themes and settings for each site. But the nice thing is that once you are logged into one site, you are also logged into the other. Note, depending upon your server config, you may need to use the $base_url and $cookie_domain variables to make this work properly.

More details on advanced Multisite installations are available at http://drupal.org/node/346385

Domain Access Module

So, what if you are not comfortable with all of this setup? Or, what if you want to have a single place to generate content for multiple sites? Well, the Domain Access module might be a good fit for you. This module allows you to create and configure new sites through a centralized interface. One of the nice features is that it allows users to publish content on one or many sites simultaneously. You can also do things like create subdomains for users, set different themes for each new site, alias sites, create unique navigation for new sites and lots more.

The major difference between this module and the default Drupal Multisite handling is that Domain Access uses one database for all of the sites and uses module code to handle permissions, etc. The most apparent strengths seem to be a) the ability to publish on multiple sites from one page, and b) managing sites using a Drupal interface. Drupal Multisite, on the other hand determines the settings.php based on the URL string and uses Drupal core to determine permissions and other settings. The big strengths of the default Multisite method are a) the ability to use separate databases and b) you are always using Drupal core to handle things like permissions.

In order to determine the right solution, you should start by asking the following questions:

  • How many sites do you have?
  • How experienced is your site administrator?
  • Is it important to use unique databases for separate sites?
  • Do you have a lot of content that will be published on more than one of your sites?

Myriad Possibilities

There are many other non-Drupal methods for managing multiple sites. For example, you could run a number of sites from one set of Drupal files by using a unique directory with symbolic links to a master set of Drupal files:

[myserver]$ cd /var/www/html/newsite
[myserver]$ ls -lhF
total 8.0K
... cron.php -> /var/www/drupal/cron.php
... includes -> /var/www/drupal/includes/
... index.php -> /var/www/drupal/index.php
...
... sites/
... themes -> /var/www/drupal/themes/
...

The abbreviated list above demonstrates how all of the Drupal "files" in this directory are actually symbolic links... except for the sites directory. You could create as many of these directories as you need, provided that they all have a unique sites directory. This option would allow the updating of multiple sites by replacing one set of core files. The big caveat here is that if Drupal changes any core files to which you have symbolic links, you will have to alter every one of your sites to match.

While I'm on the symbolic link topic, be sure to check out JAM's tip on quick upgrades using symlinks.

For more complex setups you could explore code repositories or package managers. Whatever method you choose, Drupal is well suited to handle multiple sites with a very simple upgrade path. As long as you keep all of your customizations in the sites directory, it will be a snap to upgrade two or two hundred sites... well, two hundred might not exactly be a snap, but you get the point :)

Comments

Posted on by Boris Mann (not verified).

I know it's a big topic, but you really should at least mention that you're likely to need to edit / update your httpd.conf and/or a vhosts.conf file as part of step 5. That whole step hides a lot of complexity and is usually where things go wrong.

As well, you might want to add a link or mention of Aegir, the mass hosting management system. Domain Access is an excellent solution, but it is a very specific "multiple site" solution -- if you want to share content, use Domain Access. Maybe I'll get around to writing my own post on the subject some time soon :P

Posted on by Kevin Hankens.

Hey Boris! Thanks for the note :)

I'm glad that you mentioned Aegir. For those not familiar, check out the Drupal.org Group. I feel bad about not giving it proper mention, because it provides some great site management tools.

It definitely deserves a unique blog post, so if you do something, make sure to come back here and link to it! BTW, congrats on the RC1 release, looks like you guys are making good progress!

Also, regarding VirtualHost configuration in Apache, here is a very basic example to illustrate Boris' point:

NameVirtualHost *:80

# You could use multiple VirtualHost stanzas, each pointing
# to the same DocumentRoot, or use multiple ServerAlias
# definitions if you don't need any unique site settings
<VirtualHost *:80>
  ServerAdmin user@example.com
  DocumentRoot /var/www/html/site

  # All of these domain names will point to the same
  # DocumentRoot - /var/www/html/site and Drupal will
  # sort out which settings.php file to use
  ServerName example1.com
  ServerAlias www.example1.com
  ServerAlias example2.com
  ServerAlias www.example2.com
  <Directory "/var/www/html/site">
    Options Indexes FollowSymLinks MultiViews
    AllowOverride All
    Order allow,deny
    Allow from all
  </Directory>

</VirtualHost>

The main point being that every domain that uses Drupal Multisites has to point to the same Drupal directory (DocumentRoot) and use the same index.php for Drupal to figure out how to sort them out.

Note that this same concept holds true for using Multisite with subdirectories. This is why you need to make a symbolic link from inside the Drupal directory to the drupal directory itself.

Posted on by Boris Mann (not verified).

Just to hammer the point home with the example above, Step 5 (a) should read --

"Edit your httpd.conf file to include a ServerAlias line for each domain or subdomain in your multisite install"

Note that I wouldn't add both the www and the main site, that should be handled elsewhere, otherwise it just hits .htaccess and then chews resources redirecting. It's also confusing because it doesn't show an example of a subdomain.

Anyway, I'm just being picky because the largest problem that people have with this is usually DNS / httpd.conf related.

Re: Aegir -- yep, Adrian just did a great release, and did some really great improvements to drush. So props to him, not to me -- I'm just an interested community member at this point.

Posted on by gnozu (not verified).

It's good to see a clear summary of a topic which is not particularly well documented or publicised in the world of Drupal. The institution I work at is currently looking at using Drupal in a fairly big way, possibly across dozens of departments and hundreds of users. So I'm very interested in the potential for multisite Drupal.

Using symlinks is a quick and handy way of getting subdirectories to work in multisite, but it's really not too neat and I suspect not desirable for many other large-scale deployments (eg how do you get sub-sites of each site?) We're looking at a possible solution mixing subdomains and apache's mod_proxy, so to the end-user it looks like each site is a subfolder, when really it's a subdomain.

Posted on by Kevin Hankens.

Hey Matthew, awesome point. It's definitely worth taking a minute to discuss further.

If you have 100 symlinks in your Drupal root directory, it's going to get ugly quick. Not to mention, cumbersome to upgrade, which defeats the whole purpose. Here's a very hackish example using mod_rewrite and mod_proxy to show some of the hurdles:

  1. Set up a Drupal installation for dom1.com
  2. Set up a multisite installation for subdirectory.dom1.com (which will later get rewritten from dom1.com/subdirectory)
    • create /sites/subdirectory.dom1.com
    • configure your apache VirtualHost stanza to send dom1.com and subdirectory.dom1.com to the same Drupal directory
    • install the second site at http://subdirectory.do m1.com/install.php
  3. Enable Apache's mod_proxy module
  4. Create a mod_rewrite directive to send dom1.com/subdirectory to subdirectory.dom1.com
  5. Create a conditional $base_url in your settings.php file. Because of the proxy redirect, you won't be able to log into dom1.com/subdirectory - you will have to administer the site from subdirectory.dom1.com.

WARNING: This is just to demonstrate the complexity. I was able to get this working in a sandbox environment, but there are still plenty of pitfalls.

Here's the code from you httpd.conf:

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so

NameVirtualHost *:80

<VirtualHost *:80>
DocumentRoot /var/www/html/site

# Send all of the domain names to the same Drupal
# installation
ServerName dom1.com
ServerAlias subdirectory.dom1.com

<IfModule mod_rewrite.c>
   RewriteEngine On

   # If the request is not for subdirectory.dom1.com, then
   # use mod_proxy (note the "P" flag in the rewrite rule)
   # to send the request to subdirectory.dom1.com
   RewriteCond %{HTTP_HOST} !^subdirectory\.dom1\.com$ [NC]
   RewriteCond %{REQUEST_FILENAME} ^\/subdirectory(.*)$
   RewriteRule ^.*$ http://subdirectory.dom1.com%1 [L,P]
</IfModule>

<Directory "/var/www/html/site">
   Options Indexes MultiViews FollowSymLinks
   AllowOverride All
   Order allow,deny
   Allow from all
</Directory>

</VirtualHost>

Then in your sites/subdirectory.dom1.com/settings.php file:

Again, the big crux here is that you cannot log into dom1.com/subdirectory, so to administer the site, you need to log into subdirectory.dom1.com - in which case you don't want to use a $base_url.

One major hurdle that I can see right away is needing to list all of your subdirectories in the mod_rewrite stanza. It could probably be simplified with a good regular expression though. If you have any suggestions about how you are approaching it, we'd love to hear! :)

Posted on by gnozu (not verified).

Our setup is still very much a test, but we seem to be getting what we need through a mix of apache Aliases and mod_proxy. Specifically something like:
ProxyPass /site1 http://site1.mysite/site1ProxyPassReverse  /site1 http://site1.mysite/site1
And then a separate VirtualHost for each site, like this:
DocumentRoot /path/to/webroot/mysiteServerName site1.mysiteRedirectMatch 301 ^/$ http://mysite/site1Alias /site1 /path/to/webroot/mysite

$base_url needs to be set to http://mysite/site1

Admittedly this means a separate VirtualHost for each of the dozens/hundreds of sites, but we can live with that.
Plus points:
+we can have subsites like http://mysite/site1/site2
+we can have single sign on. Sign on to site1 and you're signed on to all the sites, including all the admin pages for all the sites (if you have permissions set).

By the way, all this can be avoided by changing just one line in the Drupal core (line 239 in bootstrap.inc) to use $_SERVER['REQUEST_URI'] instead of $_SERVER['SCRIPT_NAME']. You could then just use Aliases and avoid symlinks and mod_proxy altogether.
However I'm too much of a Drupal newbie to know why this line is the way it is. I'm sure it's for a very good reason, so better to leave well alone.

I'll hopefully be testing this on something more than my localhost soon, so fingers crossed.

Posted on by green (not verified).

i am using domain access module with single sign on.
consider my main site is -> localhost
and my subdomains are test.localhost,test1.localhost,test2.localhost.
i want the users to b signed only to the site in which they r registered.

for eg if user1 s registered in test.localhost and test1.localhost he shd b available only in localhost,test.localhost and test1.localhost. Not test2.localhost......

I urgently need Any suggesstions for the above.
pls provide nay related link to this also.

Thanks in advance....