Flat Multi-Environment Config for Craft CMS 3
Tags: craftcms, config, env
Published: 2020-03-23 21:11:16

---
title: Flat Multi-Environment Config for Craft CMS 3
published: true
date: 2020-02-29 15:29:00 UTC
tags: craftcms,config,env
canonical_url: https://nystudio107.com/blog/multi-environment-configuration-for-craft-cms-3
---

# 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

Andrew Welch / [nystudio107](https://nystudio107.com)

![Multiple environment configuration](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_1200x675_crop_center-center_82_line/multiple-environment-configuration.jpg)

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:

- <kbd>dev</kbd> — your local devel­op­ment environment
- <kbd>staging</kbd> — a stag­ing or User Accep­tance Test­ing (UAT) serv­er allow­ing stake­hold­ers to test
- <kbd>production</kbd> — 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 <kbd>dev</kbd> you might want debug­ging tools enabled, but not in live <kbd>production</kbd>
- **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 <kbd>dev</kbd>, but you prob­a­bly do in live <kbd>production</kbd>

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](https://www.freecodecamp.org/news/how-to-securely-store-api-keys-4ff3ea19ebda/), 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.

## Enter the .ENV file

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

![Storing secrets env file](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_1200x675_crop_center-center_82_line/storing-secrets-env-file.jpg)

This <kbd>.env</kbd> 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

```

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](https://github.com/docker/compose/issues/3702).

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

<aside>
                                Adding com­ments to your .env file is being nice to future-you
                            </aside>

While there is some [debate over the effi­ca­cy](https://blog.fortrabbit.com/how-to-keep-a-secret) 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](https://www.heroku.com/), [Dock­er](https://www.docker.com/), [Buddy.works](https://buddy.works/), [Forge](https://forge.laravel.com/), and many oth­er tools work direct­ly with <kbd>.env</kbd> 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](https://github.com/nystudio107/dotenvy) for details on automat­ing that.

It’s a good prac­tice to pro­vide an <kbd>example.env</kbd> 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

```

The <kbd>example.env</kbd> 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 <kbd>.env</kbd> when con­fig­ur­ing the project for a new envi­ron­ment. I use the [scream­ing snake case](https://dev.to/fission/screaming-snake-case-43kj) con­stant <kbd>REPLACE_ME</kbd> 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.

## Envi­ron­ment Vari­ables 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](https://docs.craftcms.com/v3/config/environments.html) 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](https://github.com/vlucas/phpdotenv) library for <kbd>.env</kbd> file han­dling. In fact, in the <kbd>web/index.php</kbd> 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 <kbd>Dotenv</kbd> class exists, will look for a <kbd>.env</kbd> file in the project direc­to­ry (set by the con­stant <kbd>CRAFT_BASE_PATH</kbd>) and try to load it.

What this actu­al­ly does is it calls the PHP func­tion [putenv()](https://www.php.net/manual/en/function.putenv.php) for each key/​value pair in your <kbd>.env</kbd> file, which sets those vari­ables in PHP’s <kbd>$_ENV</kbd> superglobal.

The <kbd>$_ENV</kbd> super­glob­al con­tains vari­ables from the PHP run­time envi­ron­ment, and the <kbd>$_SERVER</kbd> super­glob­al con­tains vari­ables from the serv­er envi­ron­ment. The PHP func­tion [getenv()](https://www.php.net/manual/en/function.getenv.php) reads vari­ables from both of them of these super­glob­als, and is how you can access your <kbd>.env</kbd> envi­ron­ment variables.

<aside>
                                <span>“</span>Super­glob­al” just means it’s a glob­al vari­able defined by <span>PHP</span>, and avail­able in every script. It isn’t faster than a speed­ing bul­let or anything.
                            </aside>

So if our <kbd>.env</kbd> 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

```

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

![Craft cms environment variables autocomplete](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_992x516_crop_center-center_100_line/craft-cms-environment-variables-autocomplete.png)

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') %}

```

## Alias­es 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](https://www.yiiframework.com/doc/guide/2.0/en/concept-aliases).

<aside>
                                Yii<span>2</span> is the webapp frame­work that Craft <span>CMS</span> is built on
                            </aside>

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 <kbd>config/general.php</kbd> file in the <kbd>aliases</kbd> 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 <kbd>@web</kbd> and <kbd>@webroot</kbd> 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](https://docs.craftcms.com/v3/sites.html#creating-a-site).

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

```

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

```

To resolve an alias from Twig:

```

{% set path = alias('@webreoot/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:

![Craft cms aliases autocomplete](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_992x516_crop_center-center_100_line/craft-cms-aliases-autocomplete.png)

## 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()](https://docs.craftcms.com/v3/dev/functions.html#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 <kbd>parseEnv()</kbd> 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.

## Con­fig files in Craft CMS

Craft CMS also has the con­cept of [con­fig files](https://docs.craftcms.com/v3/config/environments.html#config-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 <kbd>*</kbd> key is **required** for a con­fig file to be parsed as a mul­ti-envi­ron­ment con­fig file. If the <kbd>*</kbd> 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 <kbd>CRAFT_ENVIRONMENT</kbd> con­stant, which is set by:

- The <kbd>ENVIRONMENT</kbd> vari­able in your <kbd>.env</kbd>, 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.

<aside>
                                Flat is beautiful
                            </aside>

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

## 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](https://github.com/nystudio107/devmode).

![Flat is beautiful](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_1200x675_crop_center-center_82_line/flat-is-beautiful.jpg)

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 <kbd>.env</kbd> file.

<aside>
                                Using flat con­fig files with envi­ron­ment vari­ables keeps all the per-envi­ron­ment set­tings in one place
                            </aside>

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 <kbd>example.env</kbd> 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](https://docs.craftcms.com/v3/project-config.html) 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 <kbd>.env</kbd> 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.

<aside>
                                While this .env file may look long, remem­ber that it’s con­sol­i­dat­ing all of the envi­ron­ment vari­ables in one place
                            </aside>

Note also that the <kbd>.env</kbd> 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 <kbd>config/general.php</kbd> 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'),
    'siteUrl' => getenv('SITE_URL'),
    // 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 <kbd>aliases</kbd>, are all set from <kbd>.env</kbd> envi­ron­ment vari­ables via <kbd>getenv()</kbd>.

Note that we’re explic­it­ly type­cast­ing the boolean val­ues with <kbd>(bool)</kbd> because they are set with either <kbd>0</kbd> (false) or <kbd>1</kbd> (true) in the <kbd>.env</kbd> file, because <kbd>true</kbd> and <kbd>false</kbd> 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](https://docs.craftcms.com/v3/config/config-settings.html) page.

Let’s have a look at the <kbd>config/db.php</kbd> 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 <kbd>.env</kbd> envi­ron­ment vari­ables via <kbd>getenv()</kbd>.

Final­ly, let’s have a look at the <kbd>config/app.php</kbd> file that lets you con­fig­ure just about any aspect of the [Craft CMS webapp](https://www.yiiframework.com/doc/guide/2.0/en/structure-applications):

```

<?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](https://dev.to/gaijinity/enhancing-a-craft-cms-3-website-with-a-custom-module-7k7) article.

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

<aside>
                                This is play­ing Craft in <span></span>​<span>“</span>hard” mode
                            </aside>

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](https://redis.io/), 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](https://servd.host/blog/adding-redis-to-craft-cms) article.

## Mul­ti-site Mul­ti-Envi­ron­ment in Craft CMS

Craft CMS has pow­er­ful [mul­ti-site](https://docs.craftcms.com/v3/sites.html) 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 <kbd>siteUrl</kbd> in your <kbd>config/general.php</kbd> changes from a string to an array:

```

    'siteUrl' => [
        'en' => getenv('EN_SITE_URL'),
        'fr' => getenv('FR_SITE_URL'),
    ],

```

The key in the array is the lan­guage han­dle, and the val­ue is the <kbd>siteUrl</kbd> for that site.

And your <kbd>.env</kbd> would have the cor­re­spond­ing URLs in it:

```

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

```

You can have a sep­a­rate <kbd>.env</kbd> envi­ron­ment vari­able for each site as shown above, or if your sites will all have the same base URL, you can define an alias:

```

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

```

And then your siteUrl array would just look like this:

```

    'siteUrl' => [
        'en' => '@baseSiteUrl/en',
        'fr' => '@baseSiteUrl/fr',
    ],

```

This makes it a lit­tle clean­er to set up and main­tain, and it’s few­er envi­ron­ment vari­ables you need to change.

## Wind­ing Down

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

![Winding down](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_992x558_crop_center-center_82_line/winding-down.jpg)

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](https://github.com/nystudio107/craft). Enjoy!

## Further Reading

_If you want to be notified about new articles, follow [nystudio107](https://twitter.com/nystudio107) on Twitter._

<small>Copyright ©2020 nystudio107. Designed by nystudio107</small>