There has been a boatload of discussion amongst the
Drupal community regarding best practices for
production environments with a Drupal
codebase. The reason this is usually
a sore subject for many Drupalers lies in Drupal's heavily database
dependent site configuration and management. Thus, it becomes more
difficult to manage Drupal sites across different development
environments with the tools typically used for this.
Software to help us
Many developers are used to managing software codebases with either
are revision control
systems). These systems make it easy to manage file-based software
releases, rollbacks, development branches, etc. However, because of
Drupal's database-usage strategy, managing and moving around Drupal
codebases is not as beneficial as it is with other software.
So what exactly are the options we have for managing our sites? Well,
I'm going to run through the process by which we are currently managing
a live site between development and production (sans-staging). I've also
linked to many other articles and theories on this topic at the end of
A few months ago, I wrote a blog post on Painless Drupal revision
control with CVS and Subversion on a shared
That post is an good read for those interested in simply getting up and
running with a Drupal codebase utilizing CVS and Subversion for local
revision control, as well as easy upgrades from Drupal.org's CVS. While
that blog post focuses on simply getting setup, this post will be more
geared towards the issues we currently face with that setup, the
proposed workarounds, and the strategies we personally implement.
A sample scenario
I'll start off with the site that we're currently in active development
with, and have also already launched. The site is currently sneaking by
under the radar, and we're going to keep it like that for a while, so
we'll refer to the site as 'Project X'.
Project X began life as a simple CVS checkout from Drupal.org onto my
local machine. At that same time, I also ran CVS checkouts of all the
modules that I knew I would need for the project. When I had that fairly
set, I imported the entire project into our corporate Subversion
repository. I then deleted the codebase from my local machine, and
checked out a working copy of the project back onto my machine. The
project is being developed by myself and one other developer, so he
checked out a copy to his local machine as well.
I went about installing Drupal as normal, knowing that I'd be storing
development connection settings in our /sites/default/settings.php. This
is so when we release the software, we would be more specific with the
definition of the settings.php file in /sites/projectx.com/settings.php.
With that setup, we can retain the same codebase for both development
and production environments. Drupal will look for 'projectx.com' on both
servers (dev and prod), but since the development 'servers' are simply
our local machines, it will fall back to /sites/default. Within our
/sites/default/settings.php, we pointed the database to a MySQL server
we run in-house that we can both connect to.
At this point, it should be noted that the codebase we both have checked
out is from 'trunk'. We
always develop on trunk. That is, of course, until we have a reason to
branch off into separate
branches. This is a
smaller project, however, so we simply build on trunk for now.
Drupal configuration and customization
I go about building the theme within my trunk checkout, committing
changes, adding files, etc. Our other developer, we'll call him
'Pete', is hacking away at a new module we're
building to take care of some special functionality. He's commiting his
changes, too. Every once in a while, we'll tell each other to run
updates to grab the latest code from trunk. This is especially important
when adding new modules. If you need to add a new module into trunk,
download (or CVS checkout) the module into your codebase, then add and
commit to trunk. Before you enable the module, tell Pete to run an
update on his codebase (he'll probably have no clue what you're talking
about). We don't want to go about enabling a module, resulting in the
database change, and have Mr. Pete access the site with the now enabled
module in the database, but no module files to support it. In fact, I'm
not sure really what would happen, perhaps a black hole, probably
nothing. Either way, I'll leave it to someone else to find out.
That's pretty much it for developing pre-production. We make our
changes, have our fun, build some stuff, etc. The fun times come for
when we want to launch the site on our production server.
Before you release your first version, you'll want to setup your
settings.php for the production site. Create the directory
/sites/projectx.com and copy the 'settings.php' from /sites/default to
the directory. Modify the projectx.com settings.php, specifically the
$db_url (line 93 for Drupal 6). Set the correct DB connections here to
your production database. That'll be it for the settings.php file.
Now you'll need to dump and import the development database into the
production database that you've setup. Since this is the first release,
you don't need to worry about overwriting anything.
Tagging our first release
Once we've finished developing on our local machines, have duplicated
the development database to the production database, and have finished
our final commits from both machines to the repo, we're ready to
checkout a copy onto the production server. However, before we do that,
we should keep in mind our future development patterns. We will surely
want to be able to continue developing on trunk while not having to
worry about our production codebase. For that, we use 'tags'. Each time
we have a software release we feel is ready for production, we release a
new version, and switch the production version to use the latest
The quickest way to do this is to SSH to the server that hosts your
repositories. The following command (svn
copy) will copy your
current trunk build to your very first tag:
Once that's set, you're ready to checkout the tagged release to your
production server. Head over to the server, and checkout the 1.0.0
If you've setup the settings.php correctly, the site should be good to
go. That's it for the initial launch. The site's done, right? Wrong.
Now that the site is live and accumulating data, we need to change our
development habits. The development database is no longer the 'master',
as there have been changes to the production database that we don't want
to overwrite with development data. While we haven't devised a brilliant
solution for merging development and production data, we've realized
that we don't really need to.
When we're ready to begin a new 'development cycle', we clone the
production database, and completely dump and rebuild the development
database with the production database. I wrote a stupid quick
production to development bash
handle this for us. Much easier than doing it manually, anyways. This is
by no means a cutting-edge development process, but it seems the most
logical for us. This is a fairly small project that doesn't really
warrant some of the more in-depth development environments that I've
linked to at the end of this article.
So now that we've cloned the production DB to the development DB, we've
got all of the content available to us for testing with. The majority of
our development is done in two areas:
- Theme development
- Custom module development
Theme development is heavily (if not all) file-based, so this
development strategy caters well to that. Custom module development is
heavily file-based, but can also be heavily database dependent. We find
that, while not having a solid development to production database
migration process, manually setting up the module in production really
isn't that much work. When I first delved into this problem, I wanted a
solid, complete and foolproof solution to migrating development database
changes to production. Unfortunately, that just isn't available, and
once I came to terms with that, I realized I'm not all that upset about
If you develop often, and release often, you'll probably agree with me.
Surely, if you're building 4 new themes, 20 new modules, installing 6
contrib modules, and expecting to not have to do any work when migrating
to production, you're in for a treat. If you're doing that, however,
shouldn't you have rolled that into your initial release?
Ah, I digress. So that's our general strategy. So what happens when
we're ready to release our new-fangled changes on development?
When we release upgrades to the software, we simply create a new tag.
When you're ready to tag the current trunk build as a new release,
Once you've done that, you're ready to upgrade your production checkout
to the latest release. But, how?
We use the 'svn switch'
method. Essentially, we're switching a current working copy to a new
subversion project URL. Subversion takes care of the changes between
those URLs, with the 'switch' command. When ready to release 1.0.1, we
head to the production server working copy and:
Subversion makes the appropriate changes to the working copy to reflect
changes made from REL-1-0-0 and REL-1-0-1. Win.
Managing production filesystem changes
So now we're done, right? Not really. What happens if there are changes
to the filesystem on your production server, such as user file uploads,
pictures, etc? Let's say Jon uploads a
picture of a drunk cat for his profile picture. We want those file
changes to be stored in our repository, as well. You might not want to,
and if that's the case, you can skip this part. If you do, that's where
'svn merge' comes in
handy. The merge command will essentially 'merge' differences between
two sources into a working copy.
Before you can merge the changes, you need to commit the appropriate
changes you want merged to your tagged release. From the production
checkout, run 'svn add'
on the files that were added. Then, commit your changes. Be careful to
not commit file modifications that you did not specifically want merged.
You'll need to run the merge from a trunk checkout, since you want to
merge the changes from a tagged release into trunk. From a working copy
You'll note the use of '--dry-run'. Run the command once as a 'dry run'
to see the changes before you actually do them. This is very useful.
When you're satisfied with the file changes, remove the '--dry-run' and
With an 'svn status',
you'll see the local file modifications to your trunk checkout. If
you're still happy, commit the changes to trunk, and you're done.
I always do the merge after (and only directly after) I upgrade the
production copy to the latest tagged release. That way, the changes from
tag to trunk only include the file changes or additions that occurred on
production, and not file changes on trunk.
So that's about it for our entire development lifecycle.
The above solution will probably only suffice for small-scale Drupal
productions. It may or may not be what you're looking for. Fortunately,
there are many brilliant minds in the Drupal community, and there are
quite a few alternatives for 'development to staging to production