Deploying your Drupal website to production requires some planning. Performance is key, so you need to have JavaScript and CSS pre-processing turned on. If you use the Advanced Aggregation module, its features should also be turned on. And of course, page caching should be turned on as well. In addition, small details need to be addressed, like suppressing views debug data and defining your $base_url variable for your production domain.

Managing site performance settings are important for production, but it is a different story in your development environment. For example, you typically would want caching turned off and JavaScript and CSS native (not minified or combined) for debugging purposes. Configuration settings would be cumbersome to have to manage manually, so you should use a system that can set these values automatically per environment for you when you deploy.

The Drupal 8 Configuration Ecosystem

It is important to understand how the Drupal 8 configuration ecosystem works, and how to manage configuration from both a module and whole site perspective. There have been tremendous improvements implemented over previous versions. The system is extremely flexible and can be managed in many ways; but with flexibility comes complexity which can cause some confusion because there is no "best" or "recommended" workflow.

I use deployment scripts, which I run immediately after a site push, to ensure that the site is properly configured after changes. This type of script is the key to continuous integration and automated builds. In order to implement this technique, the site needs to be set up to use certain methodologies.

  • The site configuration resides in the database
    The main configuration of the site is managed in the database, and techniques such as changing configuration values using Drush are not used. In fact, the configuration in the database is identical in every environment. Instead, the configuration values are overridden on a per-request basis. This consideration allows more flexibility from a database perspective, so replication can be used for high availability.
  • The website is environment aware
    Your code should know what environment it is running in (development, staging or production) in order to react differently as needed. This can be achieved using server-specific configuration values.
  • Deployments are managed through scripts
    Any server based language will do, like Bash, Perl or Python. I prefer Perl, and find it easy to maintain reusable libraries of code for multiple site deployments.

Server Setup

To make your site "environment aware", you can set an Apache environment variable which declares the local environment. This variable will now be available during each request.

# /etc/httpd/conf/vhosts/website.conf 
SetEnv ENVIRONMENT production

settings.environment.php

The deployment script will generate the new settings file per environment during deployment, which I call settings.environment.php to avoid potential permissions problems or conflicts with existing scripts. As you know settings.php is called on every request, so it needs to remain stable and unaffected. We don't want to alter settings.php directly on every code push.

# add this to settings.php 
if (file_exists(__DIR__ . '/settings.environment.php')) {       
      require_once __DIR__ . '/settings.environment.php'; 
}

Environment Planning

The configuration options will differ from site to site, depending on the modules and requirements. The following are some of the most common configuration options.

Setting Development Staging Production
Error Logging Level
What level of errors should be displayed to the user?
verbose hide (none) hide (none)
Use internal page cache? false true true
Pre-process CSS files? (combine / minify) false true true
GZip CSS file response? false true true
Pre-process JS files? (combine / minify) false true true
GZip JS file response? false true true
Show Views SQL Query? true false false
Show Views performance statistics? true false false
Allow temporary file expiration? 7 days 1 year 1 year
Show PHP errors? true false false
Disable page cache? true false false

You'll notice the staging and production configurations are the same. This should usually be the case, but it's important to make the distinction for SDLC best practices. There also may be a fringe case where the staging environment needs some specific configuration for testing.

Environment Services

The cache.backend.null service needs to be enabled in order to disable Twig's render cache. This is defined in a separate file and only included in the development environment. The development.services.yml file ships with Drupal. It is helpful to read through the file, as the Core team has provided detailed descriptions of each setting, and there are more than mentioned here. The following settings are important for this development environment configuration.

services:
  cache.backend.null:
    class: Drupal\Core\Cache\NullBackendFactory

parameters:
  twig.config:
    debug: true
    auto_reload: true
    cache: false

Environment Scripts

Each environment should have its own set of configuration override statements.

Development

# ================================================================
# In development, I prefer to have all errors displayed
# ================================================================
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);

# ================================================================
# Enable local development services.
# ================================================================
$settings['container_yamls'][] = DRUPAL_ROOT . '/sites/default/development.services.yml';

# ================================================================
# disable page cache and other cache bins
# ================================================================

$cache_bins = array('bootstrap','config','data','default','discovery','dynamic_page_cache','entity','menu','migrate','render','rest','static','toolbar');
foreach ($cache_bins as $bin) {
  $settings['cache']['bins'][$bin] = 'cache.backend.null';
}

# ================================================================
# disable advagg module settings
# ================================================================
$config['advagg.settings']['enabled'] = FALSE;

# ================================================================
# dynamic build values
# ================================================================
$base_url = 'https://dev.jimconte.com';

# ================================================================
# performance
# ================================================================
$config['system.logging']['error_level'] = 'verbose';
$config['system.performance']['cache']['page']['use_internal'] = FALSE;
$config['system.performance']['css']['preprocess'] = FALSE;
$config['system.performance']['css']['gzip'] = FALSE;
$config['system.performance']['js']['preprocess'] = FALSE;
$config['system.performance']['js']['gzip'] = FALSE;
$config['system.performance']['response']['gzip'] = FALSE;

# ================================================================
# emit views debug data
# ================================================================
$config['views.settings']['ui']['show']['sql_query']['enabled'] = TRUE;
$config['views.settings']['ui']['show']['performance_statistics'] = TRUE;

# ================================================================
# expiration of temporary upload files
# ================================================================
$config['system.file']['temporary_maximum_age'] = 604800;

Production / Staging

# ================================================================
# dynamic build values
# ================================================================
$base_url = 'https://jimconte.com';

# ================================================================
# performance
# ================================================================
$config['system.logging']['error_level'] = 'hide';
$config['system.performance']['cache']['page']['use_internal'] = TRUE;
$config['system.performance']['css']['preprocess'] = TRUE;
$config['system.performance']['css']['gzip'] = TRUE;
$config['system.performance']['js']['preprocess'] = TRUE;
$config['system.performance']['js']['gzip'] = TRUE;
$config['system.performance']['response']['gzip'] = TRUE;

# ================================================================
# emit views debug data
# ================================================================
$config['views.settings']['ui']['show']['sql_query']['enabled'] = FALSE;
$config['views.settings']['ui']['show']['performance_statistics'] = FALSE;

# ================================================================
# expiration of temporary upload files
# ================================================================
$config['system.file']['temporary_maximum_age'] = 31536000;

Putting it all together

I prefer to build the entire settings.environment.php file with each push using Perl since I write other dynamic values to the file. You can maintain a single file in php by using the apache configuration ENVIRONMENT variable as well:

if ( getenv('ENVIRONMENT') === 'development' )
{
    # put your development logic here
}
else if ( getenv('ENVIRONMENT') === 'staging' )
{
    # put your staging logic here
}
else
{
    # put your production logic here
}

This article describes only one approach to configuration management and focuses on performance. There are many other options to explore and different approaches to take based on your installed modules and site requirements.

Further Reading

Last Updated November 26th, 2020