How to Run Multiple ExpressionEngine Websites from One Set of Core Files

Garrett Henderson posted this in
Inside Rocket Media
on August 26th, 2014

At Rocket Media, we use a git submodule and lots of symlinks to separate the ExpressionEngine core files from the rest of the site-specific files. Then when it’s time to update, we check out the newest version branch and follow the appropriate EE upgrade steps.


Overall goals

Our main intent is to separate all EE core files from site-specific files so we can reuse the same set of core files to power multiple sites. This makes handling bug fixes and updates a bit easier, and keeps your site files more organized. The end goal is to use a git submodule to manage ExpressionEngine core files separately from the rest of your project files, but I’m not going to cover that part. The purpose of this article to walk you through the process of separating the core EE files so they’re reusable from site to site.

So if that interests you, let’s begin. There are a lot of separate pieces to this puzzle, so if things seem disjointed or confusing while you’re following along, hang in there! Once all the pieces are in place the full picture should become clear.


For the purpose of this article I’m assuming you know a few things:

  • You’re familiar enough with the command line to create symbolic links and set folder permissions
  • You’re familiar with the master config from Focus Lab LLC.
  • You have a web host with git and SSH access, and you have access to the folders at least one level above your webroot.
  • You’re going to be working with more than a few EE sites for an extended period of time (otherwise what I describe below probably isn’t worth the effort)

And to make all the work you’re about to do truly worth it, it would help if you know:

  • How to set up a git repo
  • How to set up and work with git submodules

Project setup

Before we get started, make sure you’re familiar with the standard EE directory structure and that you have a working copy of ExpressionEngine installed. I’ll also assume that you have the master config from Focus Lab LLC integrated into your EE site and that you have your project set up and functioning as the master config docs suggest:

├──  config                     # master config files
├──  httpdocs                   # or public_html (webroot)
|    ├──  images
|    ├──  themes
|    ├──  admin.php
|    └──  index.php
└──  system                     # EE system files

Now we’re going to move a bunch of things around. You can either clone the repo which contains the final folder structure (without the EE files), or start with a fresh project (as defined above) and follow the step-by-step instructions. The final product will look like this:

├──  ee
|    ├──  config                # master config files
|    ├──  eecore                # core EE files
|    |    ├──  images
|    |    ├──  system
|    |    └──  themes
|    ├──  template_files        # EE template files
|    └──  third_party_addons    # EE addons
└──  httpdocs                   # document root for webserver
     ├──  themes                # EE themes
     ├──  uploads               # all content uploaded from the control panel
     ├──  admin.php
     └──  index.php

Let’s get started.

  1. First, create an ee folder above webroot to hold all of the ExpressionEngine files.
  2. Move the config folder in there and set up folders for EE template files and third-party addons. You could also create any other folders you might need for snippets or Stash files.
  3. Next, move the httpdocs/images and system folders into eecore.
  4. Now, create a copy of the httpdocs/themes folder in eecore.
  5. Finally, create an uploads folder inside httpdocs.

Modify your paths

The first thing we’ll need to do is update your config.php and database.php files in ee/eecore/system/expressionengine/config/ to point to the new location of the master config files. Look for at the bottom of those files for the line that requires the config and replace with:

require $_SERVER['DOCUMENT_ROOT'] . '/../ee/config/config.master.php';

You’ll also need to update the system_path in httpdocs/index.php and httpdocs/admin.php. That should be:

$system_path = '../ee/eecore/system';

Now we need to modify a few paths in the config.master.php file. The goal here is to tell EE how to access the core and third-party themes. Core themes are those for the control panel, the wiki module, profile themes, the built-in site themes—basically any themes that ship with EE. Third-party themes are those that support third-party addons. So…

  1. Change line 96 to:
    $images_folder = 'uploads';
  2. Change lines 105 and 106 to:
    $env_config['theme_folder_path'] = $base_path . '/themes/core/'; $env_config['theme_folder_url'] = $base_url . '/themes/core/';
  3. Then add these lines directly after line 106:
    $env_config['path_third_themes'] = $base_path . '/themes/third_party/'; $env_config['url_third_themes'] = $base_url . '/themes/third_party/';
  4. Now, uncomment line 118 and change to:
    $env_config['third_party_path'] = $base_path . '/../ee/third_party_addons/';
    This tells EE that it won’t find third-party addons in the default location: ( system/expressionengine/third_party/). Remember, third-party addons are site-specific, so we want those in ee/third_party_addons/.
  5. Finally, near the bottom of the file (line 237), where we require the environment-specific config, update that path to point to the new location of our master config files:
    require $_SERVER['DOCUMENT_ROOT'] . '/../ee/config/config.' . ENV . '.php';

Now if you access your control panel, you should see the login page but all the styles will be missing. We’ll fix this in a moment.

ExpressionEngine core files

Let’s take a closer look at how we arrange the EE core files.

├── eecore
│   ├── images
│   ├── system
│   └── themes

We want to keep the contents of this folder looking as much like a freshly unzipped EE install as possible. This way when it will be very easy to prepare new EE versions for this setup. At this point, we only need to make one change to these core files.

  1. Delete the eecore/themes/third_party folder
  2. Replace it with a symlink:
    $ ln -s ../../../httpdocs/themes/third_party
    Since we’ve already told EE where to look for third-party themes (in config.master.php lines 107–108) this step isn’t strictly necessary, but we’ll do it to accommodate older addons that don’t look for a config value for their theme path.

The folder structure now looks like:

├── eecore
│   ├── images
│   ├── system
│   └── themes
│       ├── cp_global_images
│       ├── cp_themes
│       ├── javascript
│       ├── profile_themes
│       ├── site_themes
│       ├── third_party -> ../../../httpdocs/themes/third_party
│       └── wiki_themes

Okay, there’s still more to do.

Symlinks galore

Up to this point we’ve set up our project folder structure, modified config paths so ExpressionEngine knows how to find things, and configured our eecore system files so they’re reusable across multiple sites. The final step is to set up a bunch of symlinks in the httpdocs webroot.

Note: we could have left the index.php and admin.php files in ee/eecore/ and just created symlinks in httpdocs/, however we’re not. I don’t know why, it just feels wrong.


Remember back in config.master.php, line 105–106, we told EE to look for core themes in /themes/core. Let’s actually create that location.

  1. Delete all the folders in httpdocs/themes except third_party.
  2. Add a symlink called core pointing to the default themes in ee/eecore/themes. $ ln -s ../../ee/eecore/themes core

The themes folder structure should now look like:

└── themes
    ├── core -> ../../ee/eecore/themes
    └── third_party

Now if you access the control panel, it should appear as normal, with styles intact. But we’re not done yet. Remember that uploads folder we created? Now it’s time to set that up.


To keep things organized, we’re going to store all user-uploaded content in a single folder and create symlinks back to our EE core files where necessary. And for the sake of simplicity, we’ll leave the default EE images like smileys and default avatars grouped in with these files.

  1. First, copy all folders inside /ee/eecore/images to httpdocs/uploads.
  2. Delete the httpdocs/uploads/smileys folder and replace it with a symlink: $ ln -s ../../ee/eecore/images/smileys
  3. Next, delete httpdocs/uploads/avatars/default_set and replace it with a symlink: $ ln -s ../../../ee/eecore/images/avatars/default_set
  4. Finally, give all these folders write permissions with something like $ chmod -R 777 uploads (run from within httpdocs/)

The folder structure should now look like:

    ├── avatars
    │   ├── default_set -> ../../../ee/eecore/images/avatars/default_set
    │   └── uploads
    ├── captchas
    ├── member_photos
    ├── pm_attachments
    ├── signature_attachments
    ├── smileys -> ../../ee/eecore/images/smileys
    └── uploads

You’ll notice there is a httpdocs/uploads/uploads folder which is where you could configure your additional file upload locations in EE. At Rocket Media, we usually rename this to client and use that as a single file upload location or we configure multiple subfolders within the client folder. Again, the idea is to keep all user-uploaded content in a single place.

And with that, we’re done!

Final words

As I mentioned at the beginning, the end goal of all this is to manage the core files in their own repo. At Rocket Media we manage the core files with a git submodule called eecore that is composed of different branches corresponding to the EE version number, e.g. 2.7.0, 2.7.3 etc. It’s then very easy to update EE. Simply pull into your project the latest version and follow the appropriate upgrade steps. We’ve also found it useful when we need to apply a bugfix to the core files. We apply the changes to eecore, then run a git pull on the submodule for all sites affected by the bug. When a new version of ExpressionEngine is released there is a bit of work to prepare a new branch, but it’s work you only have to do once.

I hope you found this article helpful. Let me know if you have any questions or suggestions by sending me a comment.

Garrett Henderson

Lead Developer

Although he’s never attended Oxford University and has only a vague sense of its location, for some reason Garrett is one of the largest proponents of the Oxford comma (much to the disdain of many of his Rocket Media peers.) And don’t let his baby face fool you, he has been coding sites since the birth of Google. (Remember life before Google?)