WordPress Multi-Environment: Setting Up SitePoint

    Michael Sauter
    Michael Sauter

    If you’ve ever set up WordPress, you know that it’s super easy and fast to install. However, if you’ve ever tried to set up WordPress on multiple servers, deploying automatically from a repository or running on different environments, you know that these kind of tasks are a different story.

    In this article, I’ll explain how we’ve set up WordPress for the build and deployment of the new SitePoint to adapt to various environments, namely development, staging and production.

    The problem

    WordPress has a central place for its configuration: wp-config.php. This file contains all the basic information for the blog to work, such as how to connect to the database. Now, when you download WordPress, you’ll get a file called wp-config-sample.php, which needs to be copied to wp-config.php and adjusted to your setup. This works if you have just one environment (you’re not editing your files on the server, are you?) or if you take care never to overwrite the production configuration with your development one. If you have an additional staging setup, it gets quite tricky to manage.

    Ideally, we would want different configuration files for every environment, automatically loaded by WordPress. Moreover, it would be great if no files actually contain any passwords or other sensitive data so that the files can be stored safely in a repository.

    The solution

    Let’s start by creating one configuration file for every environment. Usually, you’ll have at least three: Development, Staging and Production. I’m calling these wp-config-production.php, wp-config-staging.php and wp-config-development.php. Every one of those files will contain the configuration for that specific environment. We’ll come to the content of the files later, first we need to tell WordPress how to find the right configuration file for the environment.

    The problem here is that WordPress does not know which one to load, it just loads wp-config.php. We don’t want to change the behavior of WordPress, so we’ll have to include our environment-specific configuration file from wp-config.php. In order to do that, we’ll modify wp-config.php to look like this:

    < ?php
     * The base configurations of the WordPress.
    /** Absolute path to the WordPress directory. */
    if ( !defined('ABSPATH') )
      define('ABSPATH', dirname(__FILE__) . '/');
    /** Include environment specific configuration. */
    define('WP_ENV', (getenv('WP_ENV') ?: 'production'));
    require_once(ABSPATH . 'wp-config-' . WP_ENV . '.php');
    /** Sets up WordPress vars and included files. */
    require_once(ABSPATH . 'wp-settings.php');

    Basically, we remove all content between ABSPATH and the inclusion of wp-settings.php. In between, we load the right configuration file.

    How does this work?

    The name of the environment is stored in a so-called environment variable. Environment variables are set in the virtual host configuration, which might look like this:

    <VirtualHost *:80>
            SetEnv WP_ENV "development"
            ServerName  blog.vm
            ServerAlias blog.vm
            <Directory /var/www/blog>

    WP_ENV can be read in PHP via getenv('WP_ENV'). In our example, we store that value in a constant called WP_ENV. require_once(ABSPATH . 'wp-config-' . WP_ENV . '.php'); will load the correct configuration file.

    We now need to copy all settings (except ABSPATH and the inclusion of wp-settings.php) from wp-config-sample.php into each configuration file.

    However, as I stated earlier, it would be preferable to not just enter the sensitive data directly in the file. We have two options:

    Option 1: Environment variables

    We’ve used an environment variable already to determine which environment we’re in. In the same way, we can set the database password etc. in environment variables as well and read them in via getenv. For example:

    define('DB_NAME', getenv('DB_NAME') ?: 'blog');

    This reads DB_NAME from the environment and falls back to use blog. Providing fallbacks is especially helpful when sharing the codebase with other developers. If they set up their database using the default values, everything will “just work”, without the need to change anything.

    On the other hand, if they prefer a different setup, it’s easy to change the environment values without having to touch the code, which is vital if you want to work on the same codebase.

    Option 2: Write on build

    If you’re using a build system to deploy your code, you could also use placeholders which are replaced during build, such as:

    define('DB_NAME', '##DB_NAME##');

    However, this option only works for the staging and production configurations, it’s not suited for your development configuration file.

    When you choose to use environment variables everywhere, you could even use just one configuration file that reads all values via getenv. However, I prefer having separate files. This makes it easier to see which configuration is in use for which environment. Also, you typically have some constants you only need in development, such as debugging:

    define('WP_DEBUG', true);

    That covers the setup for different configuration files. One other thing that I found really helpful when dealing with multiple environments was to set WP_HOME and WP_SITEURL in the configuration file instead of reading it from the database.


    To wrap up, here are the advantages of this setup:

    • It’s easy to see how a specific environment is configured
    • New environments can be added easily
    • Environments can use different constants
    • All configuration files can safely be committed to a repository
    • Using the environment variable setup, no credentials are stored at any time in a file