We (leafish) are currently configuring our shiny new dedicated server to host the Drupal sites we produce, and we intend to run all the sites from a single install of Drupal. Everything seems to be working perfectly, but I was wondering if anybody else has tried this on a large (20+ site) scale and run into any problems. I'd prefer to know about any issues now before we commit to this fully.

The server is running CentOS, which is functionally identical to Red Hat Enterprise. We're using Direct Admin to manage our virtual hosting and DNS. I've created two directories for the Drupal code, one for 4.6 and the other is a CVS checkout of the latest head. We're redirecting the customer's public_html directory to one of these two Drupal installations using symbolic links, and creating an entry in the Drupal "sites" directory for each of these to configure database connections and paths. In addition to the custom "modules" and "themes" directories, we're also creating a "files" directory in here for each of the sites to use.

All of this can be automated with scripts, and so far everything is running smoothly. I'd like to hear from anybody who is doing something similar who may be able to advise us on any issues they ran into. So far I'm concerned about redirecting the public_html directly, as this makes it difficult to create any custom directories within each site without configuring Apache. This may also cause similar problems if we want to create a custom .htaccess file. Paul has run into a few issues with paths, which he may comment on if he notices this post.

Thanks for any suggestions.


sami_k’s picture

Sounds cool... I don't have any suggestions, but I would like to hear of any notes for anyone looking to do similar stuff in the future.
Please read the handbook, search the forums, then ask...
http://drupal.etopian.net (Drupal Support) | http://www.drupalshowcase.com

Abilnet’s picture

I'm running quite a similar setup in dedicated CentOS / CPanel box with at least of that number of sites (some of them more than 3k uniques per day) and there has not been any problems so far. Using symlinks, too and a script to build additional sites. Hope this helps.

Geary’s picture

I've been running multiple Drupal setups with symlinks for a while now on FreeBSD and Debian systems. No problems at all here.

I don't redirect public_html (or in my case web/public), though, instead I symlink the individual Drupal files and directories inside it. I don't symlink the sites directory, so I'm not using Drupal's multi-site feature. Each site has its own sites/default/settings.php just like any ordinary Drupal site.

Everything that isn't symlinked is automatically unique to each site, just as it would be if I weren't doing the symlinking. So each site gets its own favicon.ico and files subdirectory by default, along with any other non-Drupal files and directories needed for that site.

I think this avoids all of the issues you mentioned with sharing the entire public_html. As far as Apache and Drupal are concerned, each site is a separate website just as it would be if you weren't doing the symlinking.

I share the modules and themes from the common installation, but I could always make those directories unique for a particular site by unlinking and copying them. In fact, I could put an entire site onto its own non-shared code that way--and I wouldn't have to change the site configuration at all.

I have my shared Drupal code in ~/common/drupal-production/ and the following script in ~/common/scripts. After creating a new site, I just set up the database and run this script from that site's public HTML directory:


# The Drupal source directory

check() {
    test -e $1 && {
        echo "$PWD/$1 exists, Drupal installation canceled"
        exit 1

check index.html  # not part of Drupal, but suspicious

check .htaccess
check index.php
check cron.php
check index.php
check xmlrpc.php

check database
check includes
check misc
check modules
check scripts
check themes

echo "Installing Drupal in $PWD"
echo "Linked to $source"
echo "Type OK to install:"

read ok
case $ok in
  [Oo][Kk] )
    echo "Installing now..."
  * )
    echo "Installation canceled"
    exit 2

cp $source/.htaccess .

# cp $source/favicon.ico .

test -d sites || {
    mkdir sites
    mkdir sites/default
    cp $source/sites/default/settings.php sites/default

ln -s $source/cron.php cron.php
ln -s $source/index.php index.php
ln -s $source/xmlrpc.php xmlrpc.php

ln -s $source/database database
ln -s $source/includes includes
ln -s $source/misc misc
ln -s $source/modules modules
ln -s $source/scripts scripts
ln -s $source/themes themes

echo "Drupal installed in $PWD"
echo "Now edit $PWD/sites/default/settings.php to enter your URL and database info"
echo "Also create $PWD/favicon.ico"
echo "or copy $source/favicon.ico there"
ixis.dylan’s picture

Thanks for that. I have no idea why I didn't think of linking the files instead of the whole directory. Too many late nights spent editing config files, I suspect...

demolicious | leafish

jeff h’s picture

Just a thought on this approach, and in fact the original poster's approach too. Assuming you give your clients FTP access to their web space, would it not be possible for them to delete a symlink? If yes, is it then possible that their FTP application will also delete the original files referenced by the symlinks? That's seems to be how my FTP client works (Panic Transmit). If that happened, they would bring down your entire client portfolio :o

I guess some kind of permissions could be set to protect this from happening?


ixis.dylan’s picture

Our clients don't usually require FTP access, so it's not much of a problem. If we're running all the sites from one installation of core, we cannot (and do not want to) allow people to edit the core. We provide an entire site for customers, not just a Drupal installation, and handle the administration and server-side shit for them.

If a user wants FTP access, we could either lock them into their "sites" directory so that they could still edit their theme and custom modules and upload files, or restrict them with permissions.

Setting correct permissions will stop FTP users from deleting the symbolic links. This depends on your server config, and how Apache and PHP are running.

demolicious | leafish

Geary’s picture

Not a problem. You just need to make sure that your users do not have write access to the shared source files. Typically you would do this simply by having the shared source owned by a separate user that you control.

It is true that FTP clients are often (always?) unaware of symlinks, but as long as you have your file permissions set up correctly your shared source will be safe.

astra’s picture

a good solution to create symlinks!

yngens’s picture


venkat-rk’s picture

Sorry to bump a 2 year old post (please see http://drupal.org/node/39807#comment-73433), but this is the best solution for multi site for me personally as I want each user to have their own hosting account and symlinking only the files and directories means I don't have to redirect the entire public_html to the drupal document root. Just a few doubts remain, though:

  • Files such as cron.php, update.php, xmxlrpc.php, install.php, index.php and robots.txt- is it okay to symlink them? I recollect reading somewhere that at least cron.php and robots.txt would be better off being created in the domain's public_html rather. Is this a best practice or a must?
  • It's fine to symlink the core modules, but what about the contributed modules? I can't install them in /sites/all/modules/ of the folder containing the shared codebase as the /sites directory is not being symlinked. Does this mean I have to manually copy/install them under the sites directory created in the public_html of the domain?
  • It would be great if someone could share their insights or advice on these two issues.

    Thanks in advance.

    Previously user Ramdak.

el777’s picture

What about security in this shared installation? How to separate privileges of different users from different installation?

Speed. I think this can help to improve perfomance - these scripts can be cached better than separate.

Geary’s picture

What about security in this shared installation? How to separate privileges of different users from different installation?

The Drupal databases are not shared, unless you take extra steps to share them. Each Drupal installation is completely separate from any others, except for the fact that they happen to share source code through symlinks.

If you created two completely separate Drupal installations with separate but identical source code, they would work exactly the same as two Drupal installations with this shared source code. Sharing the source code does not cause the Drupal installations themselves to be "shared" in any way.

Speed. I think this can help to improve perfomance - these scripts can be cached better than separate.

Perhaps, but it seems unlikely to make much difference. Database queries are what really take up the time in a Drupal site, and this has no effect on them at all.

el777’s picture

Thanks for the information!

I think this will be very good solution for mass-drupal hosting. Easier to maintain - you have not to upgrade dozen of sets of files. Common files could be protected from changing by users of certain instances.

johnk’s picture

I started to do something like what Geary's doing (well, exactly like what he's doing) but came across the multisite feature. That seemed to require a lot less complexity, so I went ahead with it.

So far, it's working pretty well. I've been porting sites over to Civicspace 8.2 from different versions of Drupal and CivicSpace. The sites do not share content (of course) and are on separate virtual server hosts.

The software is installed by a single symlink to the shared copy of CivicSpace (Drupal). Installation is done via a script that creates the database and makes that symlink.

Now, I'm hitting a wall. The files/ dir is shared. That's not what I want. Please, anyone, does the following sound like a reasonable way to proceed.

1. instead of symlinking the entire directory, symlink the following files individually:

2. The sites/ dir is a special case. I want to create that by copying files over. This way, each domain cannot see the other domains' config files. I think the sites/ dir is a security hole -- because you can guess the path from the site's URL, and then write a script to read the settings.php file.

3. The files/ dir is a special case, too. I want separate files/ for each site.

I'm very new to this, having done it only a half day so far. Am I thinking about this issue in the right way?

ixis.dylan’s picture

because you can guess the path from the site's URL, and then write a script to read the settings.php file.

Again, I think permissions could take care of this problem.

There's a lot of ways to doing multi-sites with symlinks. You could create the "sites" directory as a real dir, then symlink a "default" folder into it from a central repository of site configs, for example.

demolicious | leafish

laurenwestenberg’s picture

We are running a symlink Drupal multisite for a lot of customers now together with Plesk. Runs without problems and depending on your structure there are no problems with the security.
We symlinked about everything except the sites folder (so one site could be subdevided in multiple sites) and the index.php and .htaccess file.
You have to make sure users don't have the php input format ofcourse and in our situation we added a disallow for the settings.php in the .htaccess file in case the setup with chmod would go wrong.

venkat-rk’s picture

lauren, thank you for confirming this method works. I am not sure if you were actually responding to my question (http://drupal.org/node/39807#comment-685849), though.

So, if you could help with this: how does one deal with the contributed modules. The drupal directory structure has changed since Geary shared this method 2 years ago (see above: http://drupal.org/node/39807#comment-73433). Contributed modules now go into either sites/modules or sites/all/modules.

Since in Geary's method, the original sites directory in the shared codebase is not being symlinked, does this mean I should manually install modules for each domain?

Previously user Ramdak.

Zach Harkey’s picture

Venkat-rk: have you tried creating 'sites' as a normal directory, but creating 'sites/all' as a symlink to the shared 'sites/all' directory?

Update: out of curiosity, I tried this myself and it works great. You get all the benefits of your own local 'sites' directory, while still being able to leverage the shared 'all' directory from the main install.

harkey design

: z

Zygar’s picture

I'm testing out a variant of this now and it seems to work pretty nicely. It's set up like so:

Drupal installation.

User folder.

codebase obviously contains the Drupal installation.

username contains: (a * indicates symlink, otherwise it's a regular file.)
- all common drupal modules*
- any site-specific modules
update.php* (Do I even need this to be linked if I'm doing my updating centrally? I'm thinking not.)

Naturally, the "username" account has its own database, and while they can view the Drupal codebase directory, they cannot delete it - it's write protected so that only the central codebase account can change the files and update it.

Am I on the right track here? I have a VPS, and every Drupal installation I do is under a separate hosting account, so I'm thinking this is how to do it.

EDIT: One thing I'm not sure about is how, for the "Default" modules pack I use (pathauto, TinyMCE, etc) is how to preserve settings. I'd love to be able to have a whole bunch of defaults set up, so that I can install, and then run a script and said script would configure Drupal just the way I like it - take care of picking default admin theme, setting up search engine friendly urls, that sorta thing. So that I can skip all of the initial configuration and jump straight into construction. Any thoughts?

flywitness’s picture

i get loads of errors in the browser:

Warning: pg_query() [function.pg-query]: Query failed: ERROR: relation "access" does not exist in /home/drupal-6.3/includes/database.pgsql.inc on line 138

Warning: ERROR: relation "access" does not exist query: SELECT 1 FROM access WHERE type = 'host' AND LOWER('') LIKE LOWER(mask) AND status = 0 LIMIT 1 OFFSET 0 in /home/drupal-6.3/includes/database.pgsql.inc on line 159

Warning: session_start() [function.session-start]: Cannot send session cookie - headers already sent by (output started at /home/drupal-6.3/includes/database.pgsql.inc:138) in /home/drupal-6.3/includes/bootstrap.inc on line 981

Warning: session_start() [function.session-start]: Cannot send session cache limiter - headers already sent (output started at /home/drupal-6.3/includes/database.pgsql.inc:138) in /home/drupal-6.3/includes/bootstrap.inc on line 981

it works if i symlink the whole dir though. anybody know why?

conchur’s picture

Building on comments above, I've implemented a shared code-base system which keeps user data separate, allows contributed modules/themes to be shared or single-site, and allows core updates to be applied selectively. It also works with PHP user_base security, prevents users modifying shared code or symlinks, and works perfectly with user control panels like cPanel and supports any file transfer method your users want to use.

Each drupal core version is owned by a 'cms' user and installed at:

So 6.12 would be:

Each site is deployed with 2 sets of symlinks:
contains the reference to the version of drupal to be used:
/home/[username]/drupal_home/current -> /usr/local/drupal/drupal-6.12

Symlinks in here reference the drupal_home symlink above:
/home/[username]/public_html/modules -> ../drupal_home/current/modules

This means that to update this site to a new version of drupal, simply change the drupal_home link:
/home/[username]/drupal_home/current -> /usr/local/drupal/drupal-6.13

And all the public_html linked files/folders (as detailed in other posts above) will be instantly updated. The sites/all folder is symlinked to the shared codebase, and contains shared contributed modules and themes. Themes and modules unique to one site are put in the sites/default folder, so they are not visible to any other users. Likewise the files folder is only accessible by the site's user.

I've written scripts to deploy new sites (based on Geary's fantastic suggestions above) and extended them to make deployment even easier. For example the install script dumps a snapshot of a skeleton installation and uses this to create a new database for the user's site etc. I also have a skeleton file structure which is copied to the public_html directory when drupal is first setup, which contains the basic layout for the sites and files folders. The access privileges for these files and folders are then set to allow both the user and drupal (running under Apache as 'nobody' group) to read and write as appropriate. Update scripts allow you to move the drupal_home directory while maintaining or rebuilding the links, and a version changer simply updates the current version to a different specified one.

The scripts allow you to install in sub-directories of the public_html folder (or anywhere else), and optionally specify the owning user, drupal_home folder, database name, database user (if different) and various other options.

Where is the best place to post these scripts (if there is interest)? They're quite long so I'm reluctant to post them at the bottom of an already long thread...

conchur’s picture

I've started posting the scripts here.

beautifulmind’s picture

Nice information I have ever on Drupal.org.

Thank you.

webengr’s picture

Anyone able to do this for drupal 8?

metakel’s picture

I symbolic linked the /core directory, then Drupal cannot find the settings.php and goes back to the installation page again. No idea why.