Background:

We are setting up Drupal in a University environment. This means we cannot just rely on everyone playing nice.

As such, our web servers are set up so that different organization's sites run as separate users. On our legacy systems, this is done with a customized version of cgiwrap. For our new Drupal system, we are using suphp. And while we could just turn on suphp, hand out the Drupal source, and tell user to have fun, we would like to instead hand out running Drupal installs ready for them to customize. But if we are going to be supporting several dozen subsites, we would prefer to have one set of code to update, and to be able to offer "blessed" modules as globally available, including for some modules (e.g. LDAP) pre-installed and configured before we turn over the site.

That would point us in the direction of multi-site. But standard multi-site Drupal just uses symlinks to the Drupal root for subsites, which means all the php code is owned by one user, so that would not work with suphp, which runs php files as the file owner.

This is our attempt to have our cake, and eat it too. It seems to work in our test environment, but we are not Drupal experts; we only started learning Drupal last month. If you can pick flaws in this, please do! We'd rather learn about problems now than later.

The How-To:

Our environment is Drupal 7, apache2, and suphp running on Linux. If you are running something else, your mileage may vary. I assume you already have suphp working. I also assume you are familiar with doing a basic Drupal install. For this example, our base site will be www.oursite.edu. At our school, we use per-organization web users for running an organization's subsite. These are sometimes referred to as w3* users, because our convention is the usernames all start with "w3".

1. Install the Drupal source code someplace outside of the Apache webroot. In this example, I use /usr/local/drupal-7.8 to hold the Drupal 7.8 code.

cd /usr/local
tar zxvf /tmp/drupal-7.8.tar.gz
chown -R nobody:nogroup drupal-7.8

2. Create a symlink /usr/local/drupal-current pointing to the current version. This will save you effort when you need to upgrade to a new version.

ln -s /usr/local/drupal-7.8 /usr/local/drupal-current

3. You are now ready to create a multi-site subsite under the web Document Root (/var/www by default on Debian or Ubuntu, /var/www/html on RedHat). For this example, I am going to set up a Drupal site for the Paperclip Collectors Club, which will live at http://www.oursite.edu/paperclips, and run as the user and group w3paperclips. First you need to create the directories and symlinks the site will need.

cd /var/www
mkdir paperclips
cd paperclips
ln -s /usr/local/drupal-current/includes includes
ln -s /usr/local/drupal-current/misc includes
ln -s /usr/local/drupal-current/modules modules
ln -s /usr/local/drupal-current/profiles profiles
ln -s /usr/local/drupal-current/scripts scripts
ln -s /usr/local/drupal-current/themes themes
ln -s /usr/local/drupal-current/.htaccess .htaccess
mkdir sites
mkdir sites/all
mkdir sites/all/modules
mkdir sites/all/themes
mkdir sites/default

4. Now you need to put special "stub" versions of the php files in place.

echo "<?php include('/usr/local/drupal-current/authorize.php'); ?>" >authorize.php
echo "<?php include('/usr/local/drupal-current/cron.php'); ?>" >cron.php
echo "<?php include('/usr/local/drupal-current/index.php'); ?>" >index.php
echo "<?php include('/usr/local/drupal-current/install.php'); ?>" >install.php
echo "<?php include('/usr/local/drupal-current/update.php'); ?>" >update.php
echo "<?php include('/usr/local/drupal-current/xmlrpc.php'); ?>" >xmlrpc.php

5. Finally change the ownership of the whole subsite to the user you want suphp to run the site as.

cd /var/www
chown -R w3paperclips:w3paperclips paperclips

6. At this point, assuming you have the database for the site set up and ready to go, you can go to http://www.oursite.edu/paperclips/install.php and do a Drupal install.

7. Once you finish the subsite install, it is time to add a few more tweaks.

cd /var/www/paperclips/sites
mkdir www.oursite.edu
cd www.oursite.edu
mkdir files
ln -s /usr/local/drupal-current/sites/all/modules modules
ln -s /usr/local/drupal-current/sites/all/themes themes
ln -s ../default/settings.php settings.php
cd ..
chown -R w3paperclips:w3paperclips www.oursite.edu

By doing this step, you make any modules or themes installed in /usr/local/drupal-current/sites/all available to this subsite. Note that this does mean that if you have module installed in the /usr/local/drupal-current/sites/all (or "global") area, your subsite administrator will not be able to install a module of the same name. Administrators will be able to install non-conflicting modules and themes through the web interface locally (only available to their subsite), which will end up in their subsite's sites/all directory.


8. Now that you have made one site, you can make others in a similar manner (or, if you are like us, work on scripting the above steps to automate site creation). But what happens when a new version of Drupal is released? Say we want to move to 7.9?

First we install the source in /usr/local/drupal-7.9, and then we can test it out on one of our test subsites, by changing all the links/includes from /usr/local/drupal-current to /usr/local/drupal-7.9 and running update.php. After we are satisfied it works, and making sure all our backups are in place, we put each subsite offline, change the one symlink for /usr/local/drupal-current to point at /usr/local/drupal-7.9, run each site's update.php, and them put them all back online. After that, all the of the subsites will be running version 7.9.

This also has the advantage that if you have one or two subsites that (for whatever reason) cannot move to the new version, you can "pin" them to a specific version of Drupal by changing their .php stub files and symlinks to point at that particular version instead of /usr/local/drupal-current.

That is all I have for now. We would appreciate questions, comments and feedback. If you see a problem with our set up, please let us know!
Thank you.