Running a WordPress site on your local machine is a great way to do development. I’ve taken advantage of this to do development while on flights (and yes, I realize that in about 5 years it’s going to seem positively quaint that there used to be flights without Internet access).
Today, I’d like to tackle two common issues when running a WordPress site locally:
- Handling differing database connection details
- Handling plugins that can’t or shouldn’t run on a localhost
My assumptions:
- You have your site in a Git repository
- You have a working LAMP/MAMP/WAMP/whatever setup.
- You already know how to do a mysqldump and import that dump to your local machine
Database connection details
Your database user and password are (or should) be different on your localhost than they are on your production environment. One way to handle this is to have wp-config.php
not be in version control and have everyone make their own. Boo. Stop taking things out of version control. Another solution I’ve seen is that people modify the wp-config.php
and just try to be really careful not to commit their local changes. Again, boo.
Here’s how to do it. Open up your wp-config.php
file.
if ( file_exists( dirname( __FILE__ ) . '/local-config.php' ) ) { include( dirname( __FILE__ ) . '/local-config.php' ); define( 'WP_LOCAL_DEV', true ); // We'll talk about this later } else { define( 'DB_NAME', 'production_db' ); define( 'DB_USER', 'production_user' ); define( 'DB_PASSWORD', 'production_password' ); define( 'DB_HOST', 'production_db_host' ); }
Ignore the WP_LOCAL_DEV
define… I’ll explain that later.
Now open up your .gitignore
file.
/dir-that-contains-wp-config/local-config.php
There. Now you can just create a local-config.php
file and put your DB_*
defines in there. And thanks to the .gitignore
addition, you won’t have to worry about accidentally committing your local config.
But what if you want to override other defines locally? Well, just modify them in wp-config.php
like so:
if ( !defined( 'SCRIPT_DEBUG' ) ) define( 'SCRIPT_DEBUG', false );
And make sure they’re after the local-config.php
block. Now you can override these defines as well.
Plugins that are production-only
I’m a big fan of VaultPress from Automattic. But this is not something you want to run on your localhost. Other backup plugins probably fall into this category of “production-only.”
The bad way to do this is to remember to disable this plugin after you import a DB snapshot to your localhost. Boo. Remember WP_LOCAL_DEV
that we set earlier? Let’s use it.
I’ve written a quick “must-use” plugin to handle conditional plugin disabling. Get it here, and put it in the mu-plugins
directory (create it in your WP content directory if you don’t already have one).
The part you modify is at the bottom:
new CWS_Disable_Plugins_When_Local_Dev( array( 'vaultpress.php' ) );
I’ve jump-started you by putting vaultpress.php
in there. Add plugin filenames to that array as necessary. Don’t forget that most plugins are in a subdirectory, so they’ll be in the form plugin-name/plugin-name.php
.
Now those plugins won’t be active when doing local dev (based on the presence of local-config.php
).
Great post! I did something similar recently and wanted to do a blog post about it but you beat me to the punch. 🙂
I used to do it in very similar way, but for one site I needed a staging environment and then I switched to host-based guessing:
And then I have
production-config.php
,staging-config.php
andlocal-config.php
, of which only the local one is out of the version control.I know it’s a little bit more complicated, but this way I wanted to keep the staging environment in the version control and couldn’t determine where I am just based on existence of a config file.
I like the plugin disable plugin and I think it deserves a repository, not only a gist.
This is pretty sweet. I do pretty much the same thing, generally in the form of:
Then I use SANDBOX conditionally in configuration, testing (basically as if it’s an #ifdef block), a mu-plugin that handles some extra things, etc.
The plugin you wrote, now that’s pretty awesome.
Another nice tip! Thanks Nacin.
For a while I’ve been using a switch on
$_SERVER['HTTP_HOST']
inwp-config.php
. I can’t think of the downsides to it, but seeing this different method touted by much more experienced developers makes me wonder. Are there any gotchas to switching on the hostname?Depending on how you coded it, it could be potential security issue, if your web server responds to arbitrary host names. But if you do it in a switch, so that the filenames you might be including are effectively hardcoded, that should be fine. That’s similar to what Nikolay is doing above, except he’s also supporting regular expression matches.
Thanks Mark. I’m not using file includes, just different constant definitions in each case. One case for each dev server (e.g. local, dev and/or staging), then a default for the live server.
What might happen if the server responds to arbitrary host names?
The worry is that it could open up an include vulnerability.
Mark, sorry to come back to this, just want to clarify. I use a switch on
$_SERVER['HTTP_HOST']
inwp-config.php
, but I’m basically just defining the constants with different values in each case (I’m not including any files). I’m assuming the vulnerability you’re talking about would only arise if I was passing$_SERVER['HTTP_HOST']
through to aninclude
orrequire
statement? Or am I missing an aspect of include vulnerabilities here?Mark, I’m about to do a talk at WordCamp UK, and I’ll be discussing this kind of technique there. I’d love to get word from you on my previous query (comment from June 30th) so I can be sure about the security (or otherwise) of the technique I mentioned. Thanks!
I like that you have git listed up there as your first assumption.
It bums me out that there are only 3 forks of WP on github. Well, 4 now, I just forked it myself. But still, there ought to be a lot more!
Curious as to why you don’t just skip the code addition and ignore the file as you end up doing anyways.
what is the benefit of the extra work?
thanks mark,
n/m. just read up on git (svn guy thus far).
thanks for the tip mark.
good looking boy you’ve got there too 😉
thanks merci shokran !
I’ve got my production wp-config up a level on my ftp and dev, while I keep my local wp-config in its default location. On the local it finds the first copy and ignores the production. This works out for me as I can still edit both without conflict (as long as I don’t upload the local config accidentally).
The downside of completely separate files is that you can’t test changes to
wp-config.php
(a syntax error in the production one would take down the whole site).I have used svn for years and am just getting started with git. Can you recommend a good explanation of how to setup git, so that it tracks one of the official (svn) repositories for WP itself as well as the standard plugins, which I shouldn’t be editing, while giving me a local editable version of my own themes and plugins on the development site with a way to roll them out to the production one. I’m sure there must be a pretty standard way to set all that up, but I can’t find a good explanation. p.s., Good meeting you at WordCamp Phoenix and I was impressed with the professionalism and enthusiasm of all the core team, too.
I’m probably going to turn this comment into a followup blog post. 🙂
I’m personally not a fan of using SVN externals or Git submodules. Here’s what I have on my personal blog:
WordPress has three cool, but seldom-used features:
• WordPress can be installed in a subdirectory.
•
wp-config.php
can exist “a level up” from the WordPress directory.• The WordPress content directory (normally
wp-content
) can be moved, so that it does not exist within the WordPress directory.Put these three things together, and you can create a setup with a completely-unmodified WordPress directory, as I have done above.
When I want to upgrade WordPress, I do:
(The space between
http://
and the rest of the URL shouldn’t be there… WordPress.com is trying to autolink the URL and mangling it)And then on the server, I do a deploy (using Capistrano). Note that the
--all
switch forgit
tells it to remove files that have gone away. That’s why I delete thewordpress
directory first.There is one downside to this setup. Well, two. First, setting it up is more complicated. Of particular annoyance is how you have to specify your uploads directory using a relative path (relative from
wp-content
). And you have to set somedefine
s inwp-config.php
(more info). Second, some plugins won’t work. If they hardcodewp-content
anywhere in their code, they won’t work. This is becoming less and less of a problem, but you still run in to it occasionally.As an alternative, you can set it up like this:
In this setup, everything mutable lives in
/wordpress/wp-content/
, like normal. When you want to upgrade WordPress, do:(The space between
http://
and the rest of the URL shouldn’t be there… WordPress.com is trying to autolink the URL and mangling it)I specifically delete the
wp-admin
andwp-includes
folders to pick up any deleted files in there (root files don’t often get deleted) when I usegit
‘s--all
switch.Okay, this comment has gone on for far too long. I’ll turn it into a post. Coming up: how to handle plugin upgrades (hint: not manually).
I set mine up just like you said with /wordpress, /blog-content (not always called this), wp-config.php, the optional local-config.php, and index.php all in the root directory. Then I actually have a bash script that updates wp:
You can call it like this:
$ ./updatewp.sh
$ ./updatewp.sh branches/3.2
$ ./updatewp.sh tags/3.2.1
The first updates to trunk (default), the others update to the specified branch or tag.
I can’t believe after reading the comment above I still left the link in my code (Doh!). Anyway, that line should look like this without the space:
🙂
One thing I’ve done in the past is crazily parse/eval wp-config-sample.php as my “local-config.php” since that file is always in the repo. Plus, having a local DB with the sample connection details makes spinning up new sites really easy.
If you want your local config to be portable, you could check it in, and then use the presence of a
.localdev
file (git ignored, of course) to trigger its use.Of course, that forces everyone who develops on the site to use the same host/db/user/pass on their local machines. I have sites where I’m not the only contractor, and I don’t want to presume to dictate other people’s local setups.
eval()
? You’re a madman. A MADMAN, I say.Cool to see what other people are doing here. I used the switch/include method when I was working on Magento a lot, since their configs are stored in xml files instead of php files.
We use environment variables for credentials like this, including things like Amazon S3 keys. That way the config can move from environment to environment and get committed to version control with impunity. It also keeps the credentials out of the hands of 3rd parties — contractors — when we have other people work on our code.
Example:
I have a draft on my blog where I talk more about it, perhaps you’ve just inspired me to finish it up. Another thing we do is, when on a development environment we filter all the content to replace urls dynamically. This changes the production URLs to development URLs (e.g., http://www.example.com -> local.example.com). This makes it easy to pull a database snapshot from the live site, load it into a dev environment, and just have it work. It’s been great to use.
I’ve largely transitioned to using the primary domain, and using my hosts file to point it to my localhost.
Be careful with environment variables…
phpinfo()
would expose that info, right?Yeah, “disable_functions = phpinfo” in php.ini is pretty much required for storing credentials as environment variables, I really should have mentioned that.
I used a hosts file entry for a while, I’ve given myself so many near-heartattacks that I exclusively use a local domain these days. (Exception being when doing something like migrating a server.)
One way to mitigate heart attacks is to have the site print a “You are using the site locally” banner in the WordPress admin when the site is being hosted locally.
Mark,
Thanks for this! I am going to pass it along to my dev team 🙂
Interesting, I use a different way :
– wp/application/ <- wordpress with wp-config.php for local
– wp/config/environments.yaml <- a filter file with settings for 7 environments
– wp/config/environments/files/wp-config.php (and other files) <- files with markup like : ${path_upload}, ${cache}, ${db…
Then, when I deploy it, via capistrano (ex: cap deploy dev) then this copy to a temporary folder, replaces the files (wp-config.php…) with parameters from the dev environment that is in the yaml file. 🙂
Please explain this process in more detail, THANKS!
Thanks Mark! I’ll definitely be using the conditional Plugin disabling.
Sweet Mark, thanks. I like the plugin you created a lot, I certainly can find other use for it besides for switching between local and production enviroments. 🙂
Why would you include wp-admin and wp-content under version control?
I just version the theme and plugins independently and I get a lot less headaches this way.
Sometimes I have to make WordPress core modifications (only when there is no other way) temporarily. And I dislike that externals can be slow and rely on yet another server which could go down.
爱淘百科3g.my580.com,给力支持,文章不错!顶帖!
Great post and Tips for developing locally 2 things to add, 1 of which I haven’t done, but maybe you have.
1. WP has a check in the background the tries to connect to the internet (I’m assuming an auto update check), but if your not connected to the internet you get an error. I’ve been meaning to add check where if I’m not connected to the internet don’t bother trying.
2. Have you used WP+Networking+Domain mapping for local development? I’ve been using it, deploying sites and loving it.
oh and one last thing…take it even 1 step further: http://php.net/downloads.php download the php.net site and run that locally… 😀
oh sorry one more tip…I also sym link my wp-config.php file into my theme folder (since I want to jump in it, sometimes) I just add that to my .gitignore file as well.
Stupid newbie question…
Can anyone point to a good tute that covers mysqldumps? Been looking but can’t find a good one.
mysqldump –help is the primary source.
if you do not understand this, go back to
http://dev.mysql.com/doc/refman/5.5/en/mysqldump.html
If you still do not know what to do, leave it to he admin or anybody else who knows what he is doing.
Thanks Mark! These tips are really helping in my 100th iteration of local development.
I absolutely love the flow you have set up for upgrading wordpress … I’ve been really struggling with how to implement both html5 boilerplate and wordpress into my base startup projects, while maintaining the ability to easily diff and update boilerplate, and upgrade wordpress … I’m drooling at the idea of being able to use your method, BUT, my local dev is multisite with subdomains, so, there is no wp in a directory option.
Might you have any slick moves that would assist in this particular setup??? 😉
Currently I’ve just ended up setting up a remote tracking branch for both and pulling them in, hoping in the future to be able to just do a straight fetch merge on core wordpress and a fetch diff merge on boilerplate to accept or reject changes as they apply to my customized version.
Sad setup I know, but, after running around the office like a crazed monkey for the past month … getting deeper and deeper into the rabbit hole …
Take a look at the Roots theme on github. I think you’ll like it… http://www.rootstheme.com/
Thanks! I just got this up and running and finally can auto-disable W3TC on my local install. This is a huge help to making my local workflow smoother.
It would make more sense to me however to define the plugins one wants to disable in the local-config.php file rather than modifying the plugin for each plugin one wants to disable. This way the plugin would be the same on every site and I’d just have to tweak local-config.php. Is that scenario even possible?
Thanks for the awesome article. It helped me out a lot. This has been a lot more work than what I thought it was going to be, but it has been worth learning every bit of it. I bookmarked your site and I am going to share it with others people I know that want to run host their own site or would like to learn more.
Thanks,
Ricky
Thanks for this simple solution!!
Benefit’s Love Your Look makeup to match your lifestyle collection takes the guesswork out of gorgeous. Choose from three unique looks: The Lana light neutral shades, The Gabbi medium neutral shades, and The Betty deep neutral shades. Go for a “lifestyle look” or, be daring and mix and match! All of the beautiful shades for lips, eyes, and cheeks will get you the look you want! You’ll love the hidden surprise, a “beauty fortune” inside every package.
Find a new scent personality in Benefit’s sassy new collection of fragrances inspired by a whirlwind trip to the Royal Crescent in Bath, England.
thanks merci shokran !
I am using joomla. but I understood it to a new wordpress more perfect.
I will always follow you
Thanks Mark for the awesome article
sagolun
Thanks for this simple solution!
For my reference: can anyone explain why using “option_active_plugins” in the functions.php file with a simple/similar callback function to the mu-plugin above does not work. However when run from the mu-plugins folder it works perfectly?
Because plugins are loaded BEFORE the functions.php file is loaded, which means your code isn’t being processed until after that option has already been retrieved and used. MU plugins are loaded BEFORE regular plugins. Rough order for you up through ‘init’ action:
* MU Plugins loaded
* Constants (cookie and SSL) set up
* Plugins loaded
* Pluggable files loaded
* Global query set up
* Rewrites set up
* Locale loaded
* Theme set up (functions.php included)
* WP initialized
* Fire ‘init’ action
great coding mate, hope can be like you
Awesome stuff… you’ve inspired us (again). Thanks Mark!
Our latest “how to” video to translate all of this for non-technical folks.
http://labzip.com/develop-wordpress-sites-locally-then-deploy-with-git-tower-beanstalk-thanks-chris-coyier-mark-jaquith/
This is all great and I use something similar for a local -> staging -> production environment. All php require a local wp-config-local.php with environment specific info.
What’s got me stymied though, is how to keep the content in the DB’s in sync- I periodically dump from production down to the other environments, but it’s a chore. It would be great to be able to stage changes for client review/approval on the staging server (including perhaps DB & content changes) then turnkey push said updates to production.
A similar question goes unanswered on stackoverflow:
http://stackoverflow.com/questions/9406237/what-is-the-dummys-way-to-work-on-wordpress-locally-w-versioning-and-migrate
this claims to have a solution, but this methodology would just nuke any data on the production environment, not keep them synced:
http://wp.tutsplus.com/tutorials/how-to-sync-a-local-remote-wordpress-blog-using-version-control/
The only WordPress DB syncing tool I’ve heard of is RAMP from CrowdFavorite – http://crowdfavorite.com/wordpress/ramp/ (I have never used it).
site and I am going to share it with others people I know that want to run host their own site or would like to learn more.