Andrew Welch · Insights · #craft-3 #craftcms #config

Published , updated · 5 min read ·


Please consider 🎗 sponsoring me 🎗 to keep writing articles like this.

Flat Multi-Environment Config for Craft CMS 3

Mul­ti-envi­ron­ment con­figs for Craft CMS are a mix of alias­es, envi­ron­ment vari­ables, and con­fig files. This arti­cle sorts it all out, and presents a flat con­fig file approach

Mul­ti-envi­ron­ment con­fig­u­ra­tion is a way to have your web­site or webapp do dif­fer­ent things depend­ing on where it is being served from. For instance, a typ­i­cal set­up might have the fol­low­ing environments:

  • dev — your local devel­op­ment environment
  • staging — a stag­ing or User Accep­tance Test­ing (UAT) serv­er allow­ing stake­hold­ers to test
  • production — the live pro­duc­tion server

In each envi­ron­ment, you might want your project work­ing dif­fer­ent­ly. For example:

  • Debug­ging — in local dev you might want debug­ging tools enabled, but not in live production
  • Cre­den­tials — things like data­base cre­den­tials, API keys, etc. may be dif­fer­ent per environment
  • Track­ing — you prob­a­bly don’t want Google Ana­lyt­ics data in local dev, but you prob­a­bly do in live production

There are many oth­er behav­iors of set­tings that you might need or want to be dif­fer­ent depend­ing on where your project is being served from.

Addi­tion­al­ly, you may have secrets” that you don’t want stored in ver­sion con­trol, and you also don’t want stored in your database.

Mul­ti-envi­ron­ment con­fig­u­ra­tion is for all of these things.

This arti­cle dis­cuss­es the nuts & bolts of how envi­ron­ment vari­ables work, and then anno­tates a flat” mul­ti-envi­ron­ment set­up you can use today. 

Link Enter the .ENV file

Craft CMS and a num­ber of oth­er sys­tems have adopt­ed the con­cept of a .env file which for stor­ing envi­ron­ment vari­ables and secrets.

This .env file is:

  • Nev­er checked into source code con­trol such as Git
  • Cre­at­ed man­u­al­ly in each envi­ron­ment where the project will run
  • Stores both envi­ron­ment vari­ables and secrets”

It’s a sim­ple key/​value that looks some­thing like this:

# Craft database settings
DB_DRIVER=pgsql
DB_SERVER=localhost
DB_USER=project
DB_PASSWORD=XXXX
DB_DATABASE=project
DB_SCHEMA=public
DB_TABLE_PREFIX=
DB_PORT=5432

# CloudFront settings
CLOUDFRONT_URL=https://dnzwsrj1eic0g.cloudfront.net
CLOUDFRONT_DISTRIBUTION_ID=E17SKV1U1OTZKW
CLOUDFRONT_PATH_PREFIX=


The val­ues can be quot­ed or not (and indeed need to be quot­ed if they con­tain spaces), but keep in mind that if you used Dock­er, it does­n’t allow for quot­ed val­ues.

You can also add com­ments to your .env files by pro­ceed­ing a line with a # character.

Adding comments to your .env file is being nice to future-you

While there is some debate over the effi­ca­cy of stor­ing secrets in this way, it’s become a com­mon­ly accept­ed prac­tice that is good enough” for non-crit­i­cal purposes.

Addi­tion­al­ly, this sep­a­ra­tion of envi­ron­ment vari­ables & secrets from code — and from the data­base — allows for the nat­ur­al use of more sophis­ti­cat­ed mea­sures should they be needed.

Heroku, Dock­er, Buddy.works, Forge, and many oth­er tools work direct­ly with .env files. 

Envi­ron­ment vari­ables can also be inject­ed direct­ly into the envi­ron­ment via the web­serv­er and oth­er tools, check out Doten­vy for details on automat­ing that.

It’s a good prac­tice to pro­vide an example.env file with each of your projects that con­tain­ers the boil­er­plate for the envi­ron­ment vari­ables your project uses, as well as default values:

# Craft database settings
DB_DRIVER=pgsql
DB_SERVER=localhost
DB_USER=project
DB_PASSWORD=REPLACE_ME
DB_DATABASE=project
DB_SCHEMA=public
DB_TABLE_PREFIX=
DB_PORT=5432

# CloudFront settings
CLOUDFRONT_URL=https://dnzwsrj1eic0g.cloudfront.net
CLOUDFRONT_DISTRIBUTION_ID=E17SKV1U1OTZKW
CLOUDFRONT_PATH_PREFIX=

The example.env file can and should be checked into Git, just make sure it has noth­ing sen­si­tive in it such as passwords.

This gives you a nice start­ing point that you can rename to .env when con­fig­ur­ing the project for a new envi­ron­ment. I use the scream­ing snake case con­stant REPLACE_ME to indi­cate non-default val­ues that need to be filled in on a per-envi­ron­ment basis.

You’ll thank your­self the next time you go to set up the project, and so will oth­ers on your team.

Link Environment Variables in Craft CMS

In the con­text of Craft CMS, Pix­el & Ton­ic has the canon­i­cal con­fig­u­ra­tion infor­ma­tion in their Envi­ron­men­tal Con­fig­u­ra­tion guide. How­ev­er, we’re going to go into it in-depth, and pro­vide a flex­i­ble ref­er­ence implementation.

Craft CMS uses the vlucas/​phpdotenv library for .env file han­dling. In fact, in the web/index.php we can see it being used thusly:

// Load dotenv?
if (class_exists('Dotenv\Dotenv') && file_exists(CRAFT_BASE_PATH.'/.env')) {
    Dotenv\Dotenv::create(CRAFT_BASE_PATH)->load();
}

If the Dotenv class exists, will look for a .env file in the project direc­to­ry (set by the con­stant CRAFT_BASE_PATH) and try to load it.

What this actu­al­ly does is it calls the PHP func­tion putenv() for each key/​value pair in your .env file, which sets those vari­ables in PHP’s $_ENV superglobal.

The $_ENV super­glob­al con­tains vari­ables from the PHP run­time envi­ron­ment, and the $_SERVER super­glob­al con­tains vari­ables from the serv­er envi­ron­ment. The PHP func­tion getenv() reads vari­ables from both of them of these super­glob­als, and is how you can access your .env envi­ron­ment variables.

“Superglobal” just means it’s a global variable defined by PHP, and available in every script. It isn’t faster than a speeding bullet or anything.

You can see the envi­ron­ment vari­ables in the Craft CP if you go to Util­i­tiesPHP Info, and scroll down to Envi­ron­ment:

Envi­ron­ment Vari­ables in the Craft CP

So if our .env file looked like this:

# Craft database settings
DB_DRIVER=pgsql
DB_SERVER=localhost
DB_USER=project
DB_PASSWORD=XXXX
DB_DATABASE=project
DB_SCHEMA=public
DB_TABLE_PREFIX=
DB_PORT=5432

# CloudFront settings
CLOUDFRONT_URL=https://dnzwsrj1eic0g.cloudfront.net
CLOUDFRONT_DISTRIBUTION_ID=E17SKV1U1OTZKW
CLOUDFRONT_PATH_PREFIX=

Here’s what the the auto-com­plete drop­down looks like in the Craft CMS CP for the envi­ron­ment variables:

Envi­ron­ment Vari­ables in the CP

We could get a val­ue from PHP like this:

$database = getenv('DB_DATABASE');

And we could get the same val­ue from Twig like this:

{% set database = getenv('DB_DATABASE') %}

As part of the Craft set­up process — either via the ./​craft set­up con­sole com­mand, or via the web brows­er set­up—Craft will cre­ate a .env file for you with a num­ber of set­tings based on the ques­tions you answer dur­ing the set­up process:

# The environment Craft is currently running in ('dev', 'staging', 'production', etc.)
ENVIRONMENT="dev"

# The secure key Craft will use for hashing and encrypting data
SECURITY_KEY="EJRj99V-iH7ZLmX4pQcXQ3iUI6eWcATY"

# The database driver that will be used ('mysql' or 'pgsql')
DB_DRIVER="mysql"

# The database server name or IP address (usually this is 'localhost' or '127.0.0.1')
DB_SERVER="localhost"

# The database username to connect with
DB_USER="homestead"

# The database password to connect with
DB_PASSWORD="secret"

# The name of the database to select
DB_DATABASE="craft3"

# The database schema that will be used (PostgreSQL only)
DB_SCHEMA="public"

# The prefix that should be added to generated table names (only necessary if multiple things are sharing the same database)
DB_TABLE_PREFIX=""

# The port to connect to the database with. Will default to 5432 for PostgreSQL and 3306 for MySQL.
DB_PORT="3306"

DEFAULT_SITE_URL="http://craft3.test"

You are, of course, free to not only mod­i­fy any of these .env vari­ables, but also to add your own as you see fit.

Link Aliases in Craft CMS

Craft CMS also has the con­cept of alias­es, which are actu­al­ly inher­it­ed from Yii2 alias­es.

Yii2 is the webapp framework that Craft CMS is built on

Alias­es can some­times be con­fused with envi­ron­ment vari­ables, but they real­ly serve a dif­fer­ent pur­pose. You’ll use an alias when:

  • The set­ting in ques­tion is a path
  • The set­ting in ques­tion is a URL

That’s it.

Could you use envi­ron­ment vari­ables in these cas­es? Sure. But with alias­es you can do things like have it resolve a path or URL that has a par­tial path in it (see below).

You define alias­es in your config/general.php file in the aliases key, e.g.:

<?php
/**
 * General Configuration
 *
 * All of your system's general configuration settings go in here. You can see a
 * list of the available settings in vendor/craftcms/cms/src/config/GeneralConfig.php.
 *
 * @see craft\config\GeneralConfig
 */

return [
    // Craft config settings from .env variables
    'aliases' => [
        '@cloudfrontUrl' => getenv('CLOUDFRONT_URL'),
        '@web' => getenv('SITE_URL'),
        '@webroot' => getenv('WEB_ROOT_PATH'),
    ],
];

Note that we’re actu­al­ly set­ting alias­es from envi­ron­ment vari­ables! They actu­al­ly com­pli­ment each other.

Both @web and @webroot are alias­es that Yii2 tries to set auto­mat­i­cal­ly for you. How­ev­er, you should always set them explic­it­ly (as shown above) to avoid poten­tial cache poi­son­ing.

Here’s how we can resolve an alias in PHP:

$path = Craft::getAlias('@webroot/assets');

To resolve an alias from Twig:

{% set path = alias('@webroot/assets') %}

This demon­strates what you can do with alias­es that you can­not do with envi­ron­ment vari­ables, which is pass in a par­tial path and have the alias resolve with that path added to it.

You can­not do this with envi­ron­ment variables:

{% set path = getenv('WEB_ROOT_PATH/assets') %}

Sim­i­lar­ly, you can­not put this in a CP set­ting in Craft:

$WEB_ROOT_PATH/assets

Here’s what the the auto-com­plete drop­down looks like in the Craft CMS CP for aliases:

Alias­es in the CP

Craft CMS and Yii2 define a num­ber of alias­es for you already, that you’re free to use if you like:

  • @root — path to the root direc­to­ry of your project
  • @lib — path to the lib/ direc­to­ry in craftcms/cms
  • @craft — path to the src/ direc­to­ry in craftcms/cms
  • @config — path to the config/ direc­to­ry in your project
  • @contentMigrations — path to the migrations/ direc­to­ry in your project
  • @storage — path to the storage/ direc­to­ry in your project
  • @templates — path to the templates/ direc­to­ry in your project
  • @translations — path to the translations/ direc­to­ry in your project
  • @app — path to the webapp that is Craft CMS in craftcms/cms
  • @vendor — path to the vendor/ direc­to­ry in your project
  • @npm — path to the vendor/npm/ direc­to­ry in your project
  • @runtime — path to the storage/runtime/ direc­to­ry in your project
  • @webroot — path to the web/ direc­to­ry in your project (your pub­lic HTML directory)
  • @web — By default, a pro­gram­mat­i­cal­ly deter­mined URL to your web­site. You should rede­fine this to avoid poten­tial cache poi­son­ing.

So for an exam­ple project, the resolved alias­es look like this:

[
    '@root' => '/home/vagrant/webdev/sites/craft3',
    '@lib' => '/home/vagrant/webdev/sites/craft3/vendor/craftcms/cms/lib',
    '@craft' => '/home/vagrant/webdev/sites/craft3/vendor/craftcms/cms/src',
    '@config' => '/home/vagrant/webdev/sites/craft3/config',
    '@contentMigrations' => '/home/vagrant/webdev/sites/craft3/migrations',
    '@storage' => '/home/vagrant/webdev/sites/craft3/storage',
    '@templates' => '/home/vagrant/webdev/sites/craft3/templates',
    '@translations' => '/home/vagrant/webdev/sites/craft3/translations',
    '@app' => '/home/vagrant/webdev/sites/craft3/vendor/craftcms/cms/src',
    '@vendor' => '/home/vagrant/webdev/sites/craft3/vendor',
    '@npm' => '/home/vagrant/webdev/sites/craft3/vendor/npm',
    '@runtime' => '/home/vagrant/webdev/sites/craft3/storage/runtime',
    '@webroot' => '/home/vagrant/webdev/sites/craft3/web',
    '@web' => 'http://craft3.test',
]

As with envi­ron­ment vari­ables, you are free to mod­i­fy these default alias­es, or add your own as you see fit. The exist­ing alias­es are avail­able via the sta­t­ic prop­er­ty Craft::$aliases in PHP.

Link parseEnv() does both

Since it’s com­mon­place that set­tings could be either alias­es or envi­ron­ment vari­ables (espe­cial­ly in CP set­tings), Craft CMS 3.1.0 intro­duced the con­ve­nience func­tion parseEnv() that:

  • Fetch­es any envi­ron­ment vari­ables in the passed string
  • Resolves any alias­es in the passed string

So you can hap­pi­ly use it as a uni­ver­sal way to resolve both alias­es and envi­ron­ment variables.

Here’s what it looks like in Twig:

{% set path = parseEnv(someVariable) %}
{# This is equivalent to #}
{% set path = alias(getenv(someVariable)) %}

Here’s what it looks like using parseEnv() via PHP:

$path = Craft::parseEnv($someVariable);
// This is equivalent to:
$path = Craft::getAlias(getenv($someVariable));

The parseEnv() func­tion is a nice short­cut when you’re deal­ing with CP set­tings that could be alias­es, envi­ron­ment vari­ables, or both.

Link Config files in Craft CMS

Craft CMS also has the con­cept of con­fig files, stored in the config/​direc­to­ry. These can either be flat” con­fig files that always return the same val­ues regard­less of environment:

// -- config/general.php --
return [
    'omitScriptNameInUrls' => true,
    'devMode' => true,
    'cpTrigger' => 'secret-word',
];

Or con­fig files can be multi-environment:

// -- config/general.php --
return [
    // Global settings
    '*' => [
        'omitScriptNameInUrls' => true,
    ],

    // Dev environment settings
    'dev' => [
        'devMode' => true,
    ],

    // Production environment settings
    'production' => [
        'cpTrigger' => 'secret-word',
    ],
];

The * key is required for a con­fig file to be parsed as a mul­ti-envi­ron­ment con­fig file. If the * key is present, any set­tings in that sub-array are con­sid­ered glob­al settings.

Oth­er keys in the array cor­re­spond with the CRAFT_ENVIRONMENT con­stant, which is set by:

  • The ENVIRONMENT vari­able in your .env, if present
  • The incom­ing URL’s host­name otherwise

Mul­ti-envi­ron­ment con­fig files are a car­ry-over from Craft 2, and con­tin­ue to be quite useful.

Flat is beautiful

How­ev­er, we’ve moved towards flat con­fig files com­bined with .env files. Let’s have a look.

Link A real-world example

For a real-world exam­ple of using flat con­fig files com­bined with envi­ron­ment vari­ables and alias­es, we’ll use the OSS’d dev​Mode​.fm web­site.

The rea­son we’ve moved away from using mul­ti-envi­ron­ment con­fig files is sim­plic­i­ty. It takes less men­tal space to know that any envi­ron­ment-spe­cif­ic set­tings or secrets are always com­ing from one place: the .env file.

Using flat config files with environment variables keeps all the per-environment settings in one place

This will save you time hav­ing to try to track down where a par­tic­u­lar con­fig set­ting is stored in each envi­ron­ment. It’s all in one place.

Here’s what the example.env file looks like for dev​Mode​.fm:

# Craft general settings
ALLOW_UPDATES=1
ALLOW_ADMIN_CHANGES=1
BACKUP_ON_UPDATE=0
DEV_MODE=1
ENABLE_TEMPLATE_CACHING=0
ENVIRONMENT=local
IS_SYSTEM_LIVE=1
RUN_QUEUE_AUTOMATICALLY=1
SECURITY_KEY=FnKtqveecwgMavLwQnX2I-dqYjpwZMR6

# Craft database settings
DB_DRIVER=pgsql
DB_SERVER=postgres
DB_USER=project
DB_PASSWORD=REPLACE_ME
DB_DATABASE=project
DB_SCHEMA=public
DB_TABLE_PREFIX=
DB_PORT=5432

# URL & path settings
ASSETS_URL=http://localhost:8000/
SITE_URL=http://localhost:8000/
WEB_ROOT_PATH=/var/www/project/cms/web

# Craft & Plugin Licenses
LICENSE_KEY=
PLUGIN_IMAGEOPTIMIZE_LICENSE=
PLUGIN_RETOUR_LICENSE=
PLUGIN_SEOMATIC_LICENSE=
PLUGIN_TRANSCODER_LICENSE=
PLUGIN_WEBPERF_LICENSE=

# S3 settings
S3_KEY_ID=REPLACE_ME
S3_SECRET=REPLACE_ME
S3_BUCKET=devmode-bucket
S3_REGION=us-east-2
S3_SUBFOLDER=

# CloudFront settings
CLOUDFRONT_URL=https://dnzwsrj1eic0g.cloudfront.net
CLOUDFRONT_DISTRIBUTION_ID=E17SKV1U1OTZKW
CLOUDFRONT_PATH_PREFIX=

# Redis settings
REDIS_HOSTNAME=redis
REDIS_PORT=6379
REDIS_DEFAULT_DB=0
REDIS_CRAFT_DB=3

# webpack settings
PUBLIC_PATH=/dist/
DEVSERVER_PUBLIC=http://localhost:8080
DEVSERVER_HOST=0.0.0.0
DEVSERVER_POLL=0
DEVSERVER_PORT=8080
DEVSERVER_HTTPS=0

# Twigpack settings
TWIGPACK_DEV_SERVER_MANIFEST_PATH=http://webpack:8080/
TWIGPACK_DEV_SERVER_PUBLIC_PATH=http://webpack:8080/

# Disqus settings
DISQUS_PUBLIC_KEY=
DISQUS_SECRET_KEY=

# Google Analytics settings
GA_TRACKING_ID=UA-69117511-5

# FastCGI Cache Bust settings
FAST_CGI_CACHE_PATH=

Because we’re using Project Con­fig to allow us to eas­i­ly deploy site changes across envi­ron­ments, we have to be mind­ful to put things like our Craft license key, plu­g­in license keys, and oth­er secrets into our .env file

Oth­er­wise we’d end up with secrets checked into our git repo, which is not ide­al from a secu­ri­ty point of view.

While this .env file may look long, remember that it’s consolidating all of the environment variables in one place

Note also that the .env set­tings are log­i­cal­ly grouped, with comments.

Let’s have a look at how we uti­lize these envi­ron­ment vari­ables in our config/general.php file:

<?php
/**
 * General Configuration
 *
 * All of your system's general configuration settings go in here. You can see a
 * list of the available settings in vendor/craftcms/cms/src/config/GeneralConfig.php.
 *
 * @see craft\config\GeneralConfig
 */

return [
    // Craft config settings from .env variables
    'aliases' => [
        '@assetsUrl' => getenv('ASSETS_URL'),
        '@cloudfrontUrl' => getenv('CLOUDFRONT_URL'),
        '@web' => getenv('SITE_URL'),
        '@webroot' => getenv('WEB_ROOT_PATH'),
    ],
    'allowUpdates' => (bool)getenv('ALLOW_UPDATES'),
    'allowAdminChanges' => (bool)getenv('ALLOW_ADMIN_CHANGES'),
    'backupOnUpdate' => (bool)getenv('BACKUP_ON_UPDATE'),
    'devMode' => (bool)getenv('DEV_MODE'),
    'enableTemplateCaching' => (bool)getenv('ENABLE_TEMPLATE_CACHING'),
    'isSystemLive' => (bool)getenv('IS_SYSTEM_LIVE'),
    'resourceBasePath' => getenv('WEB_ROOT_PATH').'/cpresources',
    'runQueueAutomatically' => (bool)getenv('RUN_QUEUE_AUTOMATICALLY'),
    'securityKey' => getenv('SECURITY_KEY'),
    // Craft config settings from constants
    'cacheDuration' => false,
    'defaultSearchTermOptions' => [
        'subLeft' => true,
        'subRight' => true,
    ],
    'defaultTokenDuration' => 'P2W',
    'enableCsrfProtection' => true,
    'errorTemplatePrefix' => 'errors/',
    'generateTransformsBeforePageLoad' => true,
    'maxCachedCloudImageSize' => 3000,
    'maxUploadFileSize' => '100M',
    'omitScriptNameInUrls' => true,
    'useEmailAsUsername' => true,
    'usePathInfo' => true,
    'useProjectConfigFile' => true,
];

// Craft con­fig set­tings from .env variables

The set­tings under this com­ment, includ­ing the aliases, are all set from .env envi­ron­ment vari­ables via getenv().

Note that we’re explic­it­ly type­cast­ing the boolean val­ues with (bool) because they are set with either 0 (false) or 1 (true) in the .env file, because true and false are both strings. Nor­mal­ly this isn’t a prob­lem, but there can be edge cas­es with weak­ly typed lan­guages like PHP.

// Craft con­fig set­tings from constants

The set­tings under this com­ment are set­tings that we typ­i­cal­ly want to adjust from their default, but we don’t need them to be dif­fer­ent on a per-envi­ron­ment basis.

You can look up what the var­i­ous con­fig set­tings are on the Craft CMS Gen­er­al Con­fig Set­tings page.

Let’s have a look at the config/db.php file:

<?php
/**
 * Database Configuration
 *
 * All of your system's database connection settings go in here. You can see a
 * list of the available settings in vendor/craftcms/cms/src/config/DbConfig.php.
 *
 * @see craft\config\DbConfig
 */

return [
    'driver' => getenv('DB_DRIVER'),
    'server' => getenv('DB_SERVER'),
    'user' => getenv('DB_USER'),
    'password' => getenv('DB_PASSWORD'),
    'database' => getenv('DB_DATABASE'),
    'schema' => getenv('DB_SCHEMA'),
    'tablePrefix' => getenv('DB_TABLE_PREFIX'),
    'port' => getenv('DB_PORT')
];

These set­tings are all pret­ty straight­for­ward, we’re just read­ing in secrets or set­tings that may be dif­fer­ent per envi­ron­ment from .env envi­ron­ment vari­ables via getenv().

Final­ly, let’s have a look at the config/app.php file that lets you con­fig­ure just about any aspect of the Craft CMS webapp:

<?php
/**
 * Yii Application Config
 *
 * Edit this file at your own risk!
 *
 * The array returned by this file will get merged with
 * vendor/craftcms/cms/src/config/app/main.php and [web|console].php, when
 * Craft's bootstrap script is defining the configuration for the entire
 * application.
 *
 * You can define custom modules and system components, and even override the
 * built-in system components.
 */

return [
    'modules' => [
        'site-module' => [
            'class' => \modules\sitemodule\SiteModule::class,
        ],
    ],
    'bootstrap' => ['site-module'],
    'components' => [
        'deprecator' => [
            'throwExceptions' => YII_DEBUG,
        ],
        'redis' => [
            'class' => yii\redis\Connection::class,
            'hostname' => getenv('REDIS_HOSTNAME'),
            'port' => getenv('REDIS_PORT'),
            'database' => getenv('REDIS_DEFAULT_DB'),
        ],
        'cache' => [
            'class' => yii\redis\Cache::class,
            'redis' => [
                'hostname' => getenv('REDIS_HOSTNAME'),
                'port' => getenv('REDIS_PORT'),
                'database' => getenv('REDIS_CRAFT_DB'),
            ],
        ],
        'session' => [
            'class' => \yii\redis\Session::class,
            'redis' => [
                'hostname' => getenv('REDIS_HOSTNAME'),
                'port' => getenv('REDIS_PORT'),
                'database' => getenv('REDIS_CRAFT_DB'),
            ],
            'as session' => [
                'class' => \craft\behaviors\SessionBehavior::class,
            ],
        ],
    ],
];

Here we’re boot­strap­ping our Site Mod­ule as per the Enhanc­ing a Craft CMS 3 Web­site with a Cus­tom Mod­ule article.

Then we’re con­fig­ur­ing the deprecator com­po­nent so that if devMode is enabled, dep­re­ca­tion errors that would nor­mal­ly be logged instead cause an excep­tion to be thrown.

This is playing Craft in “hard” mode

This can be real­ly use­ful for track­ing down and fix­ing dep­re­ca­tion errors as they happen.

Final­ly, we con­fig­ure Redis, and use it as the Yii2 caching method, and more impor­tant­ly for PHP ses­sions. You can read more about set­ting up Redis in Matt Gray’s excel­lent Adding Redis to Craft CMS article.

Link Multi-site Multi-Environment in Craft CMS

Craft CMS has pow­er­ful mul­ti-site baked in that allows you to cre­ate local­iza­tions of exist­ing sites, or sis­ter-sites all man­aged under one umbrella.

In the con­text of a mul­ti-envi­ron­ment con­fig the siteUrl used to go in your config/general.php as an array of site URLs.

How­ev­er, as of Craft CMS 3.6 the siteUrl in general.php been dep­re­cat­ed.

Instead you should define envi­ron­ment vari­ables in your .env file for your site URLs.

If they all have the same base URL, you can define just one envi­ron­ment variable:

# Site URL
SITE_URL=https://example.com/

Then define an alias in your config/general.php file:

    'aliases' => [
        '@web' => getenv('SITE_URL'),
    ],

The Base URL set­ting in Set­tings → Sites

Then in Set­tingsSites in the CP, you can use this alias to set the Base URL for each site, e.g.:

  • @web/en
  • @web/fr

The Base URL then gets stored in Project Con­fig, and will prop­a­gate to the oth­er environments.

If how­ev­er you have sep­a­rate base URLs for each site, you can define mul­ti­ple envi­ron­ment vari­ables in your .env file:

# Site URLs
EN_SITE_URL=https://english-example.com/
FR_SITE_URL=https://french-example.com/

Then in your config/general.php you can define alias­es for each:

    'aliases' => [
        '@enSiteUrl' => getenv('EN_SITE_URL'),
        '@frSiteUrl' => getenv('FR_SITE_URL'),
    ],

Then in Set­tingsSites in the CP as well as in your tem­plates, you can just use the alias­es as needed.

Link Winding Down

That about wraps it up our spelunk­ing into the world of mul­ti-envi­ron­ment con­figs in Craft CMS 3.

Hope­ful­ly this in-depth explo­ration of how envi­ron­ment vari­ables work com­bined with real-world exam­ples have helped to give you a bet­ter under­stand­ing of how you can cre­ate a sol­id mul­ti-envi­ron­ment con­fig­u­ra­tion for Craft CMS 3.

If you adopt some of the method­olo­gies dis­cussed here, you will reap the ben­e­fits of a proven setup.

The approach pre­sent­ed here is also used in the nystudio107 Craft 3 CMS scaf­fold­ing project. Enjoy!