Configuration in a Deploy

config.php is committed and locks the admin, env.php holds the secrets, and app:config:import is what makes either take effect.

Syntaxbin/magento app:config:import [-n|--no-interaction]

A release should not need anyone to open the admin and retype settings. Magento supports that with two deployment configuration files. app/etc/config.php is shared configuration and belongs in source control: enabled modules (1 or 0), websites, store groups and store views, registered themes, and non-sensitive system values such as the JS and CSS merge and minify flags. app/etc/env.php is per machine and stays out of source control: database and cache hosts, the encryption key, MAGE_MODE, and secrets.

Dump once, import everywhere

bin/magento app:config:dump scopes themes system writes the current database configuration into those files. Commit config.php. On each target machine, bin/magento app:config:import loads the files into that machine's database; -n (long form --no-interaction) skips the confirmation in a script. setup:upgrade runs the same import for you, so a release with schema changes needs only the one command.

Locked, and the one thing that must not be in the file

Any path present in these files is read-only in the admin: the field greys out, which is the point of dumping it. Secrets are the exception that must never travel in the committed file. Set those with bin/magento config:sensitive:set, which writes them to env.php instead. On a non-production box, static_content_on_demand_in_production in env.php lets a production-mode server build static files on first request and skip the long deploy step; leave it off on a real store, where the first visitor would pay for it.

Example

Example · php
<?php
// app/etc/config.php - written by app:config:dump, committed to source control

return [
    'modules' => [
        'Magento_Store' => 1,
        'Vendor_Module' => 1,
        'Magento_InventoryAdminUi' => 0,
    ],
    'scopes' => [
        'websites' => [
            'base' => [
                'website_id' => '1',
                'code' => 'base',
                'name' => 'Main Website',
                'default_group_id' => '1',
            ],
        ],
        // 'groups' (store groups) and 'stores' (store views) follow the same shape
    ],
    'system' => [
        'default' => [
            'dev' => [
                'js' => ['merge_files' => '1', 'minify_files' => '1'],
                'css' => ['merge_css_files' => '1', 'minify_files' => '1'],
            ],
        ],
    ],
    'themes' => [
        // one entry per registered theme, written by the themes config type
    ],
];

When to use it

  • Shipping a new store view without anyone clicking through the admin on production.
  • Keeping payment credentials out of git while still deploying the rest of the configuration.
  • Explaining why a configuration field in the admin is greyed out and cannot be edited.
  • Cutting deploy time on a review environment by building static files on demand.

More examples

Declare one of your own paths sensitive

TypePool is how a module tells Magento which of its configuration paths are secret and which are machine-specific. A sensitive path is never written to config.php by app:config:dump; you set it per machine with bin/magento config:sensitive:set vendor_module/api/secret '...', which writes to env.php. In developer mode run bin/magento cache:flush after editing di.xml; on a production box the change arrives through the build, because di.xml is only read by setup:di:compile.

Example · xml
<?xml version="1.0"?>
<!-- app/code/Vendor/Module/etc/di.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Config\Model\Config\TypePool">
        <arguments>
            <argument name="sensitive" xsi:type="array">
                <item name="vendor_module/api/secret" xsi:type="string">1</item>
            </argument>
            <argument name="environment" xsi:type="array">
                <item name="vendor_module/api/endpoint" xsi:type="string">1</item>
            </argument>
        </arguments>
    </type>
</config>

The per-machine half of the configuration

Values under 'system' were written by config:sensitive:set or by config:set with -e (long form --lock-env), and they are read-only in the admin. The sibling option -c (--lock-config) writes to config.php instead and wins if you pass both. static_content_on_demand_in_production makes a production-mode box generate static files when they are first requested, which removes the longest step from a staging deploy. On Adobe Commerce on cloud infrastructure, the SCD_ON_DEMAND variable in .magento.env.yaml is what writes this key.

Example · php
<?php
// app/etc/env.php (abbreviated) - never in source control

return [
    'MAGE_MODE' => 'production',
    'system' => [
        'default' => [
            'vendor_module' => [
                'api' => ['secret' => '0:3:encrypted-value-here'],
            ],
        ],
    ],
    // review and staging boxes only
    'static_content_on_demand_in_production' => 1,
];

Where the import sits in a release

setup:upgrade finds and runs the config import command, so you do not need both; app:config:import on its own is for releases that change configuration and nothing else. Note what the import does not do: it ignores values supplied as environment variables. On multi-node blue/green deploys the hash check that produces the 'configuration file has changed' error can be suppressed with deployment/blue_green/enabled in env.php, which is the only supported way to silence it.

Example · bash
# on the box where configuration is authored, then commit the file
bin/magento app:config:dump scopes themes system
git add app/etc/config.php

# on every target box, inside the maintenance window
bin/magento setup:upgrade --keep-generated   # runs app:config:import itself
bin/magento cache:flush

# configuration-only release, no schema change
bin/magento app:config:import -n
bin/magento cache:flush

Discussion

  • Be the first to comment on this lesson.