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
- Configuration Management in Drupal 8: The Key Concepts (lullabot)
- Configuration API Overview (Drupal)
- Configuration override system (Drupal)
- File Based Configuration (Drupal)
- Environment-Specific Configurations for Drupal 8 (Pantheon)
-
Drupal modules that help manage configuration