Sensitive and Environment Values
Why config.php is committed and env.php is not, how to declare a path sensitive, and what config:set --lock-env does.
bin/magento config:set --lock-env <section/group/field> <value>Two files in app/etc hold configuration outside the database. config.php is shared: the same values on every environment, and it belongs in source control. env.php is per environment and holds the secrets, so it must never be committed.
Telling Magento which is which
A module declares its own paths through Magento\Config\Model\Config\TypePool in etc/di.xml. A path listed under the sensitive argument holds restricted information: an API key, a password, an address, anything personally identifying. A path listed under environment holds something that differs per machine, such as a host or a port. bin/magento app:config:dump then routes those paths into env.php and everything else into config.php.
Setting a value without the admin
bin/magento config:set --lock-env loyalty/api/key abc123 writes the value straight into env.php and makes the admin field read-only. --lock-config does the same into config.php. Both arrived in Magento 2.2.4 and replaced the older --lock, which still exists but is deprecated.
Encryption is a separate thing
Declaring a path sensitive does not encrypt it. Encryption is a field concern: type="obscure" plus Magento\Config\Model\Config\Backend\Encrypted in system.xml encrypts on save with the installation's crypt key and decrypts on load. After editing di.xml run bin/magento setup:di:compile, then bin/magento cache:flush.
Example
<!-- app/code/Acme/Loyalty/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="loyalty/api/key" xsi:type="string">1</item>
<item name="loyalty/api/password" xsi:type="string">1</item>
</argument>
<argument name="environment" xsi:type="array">
<item name="loyalty/api/endpoint" xsi:type="string">1</item>
</argument>
</arguments>
</type>
</config>When to use it
- Stop an API key from being written into the config.php you commit.
- Point staging and production at different service endpoints from one codebase.
- Store a third-party password encrypted rather than readable in core_config_data.
- Lock a value so support staff with admin access cannot change it by accident.
More examples
The field that holds the key
type="obscure" renders the input masked and redisplays the saved value as asterisks. The Encrypted backend model encrypts before saving and decrypts after loading, so core_config_data holds ciphertext and ScopeConfigInterface still returns the plain key to your code. The two go together: obscure without the backend model stores the key in clear text behind a masked input, which is the worse of both options. Apply with bin/magento cache:flush.
<!-- app/code/Acme/Loyalty/etc/adminhtml/system.xml (group only) -->
<group id="api" translate="label" type="text" sortOrder="20"
showInDefault="1" showInWebsite="1">
<label>Loyalty API</label>
<field id="endpoint" translate="label" type="text" sortOrder="10"
showInDefault="1" showInWebsite="1">
<label>Endpoint URL</label>
<validate>validate-url</validate>
</field>
<field id="key" translate="label" type="obscure" sortOrder="20"
showInDefault="1" showInWebsite="1">
<label>API Key</label>
<backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
</field>
</group>Setting it per environment
Each form of the command names the file it wrote to, which is the fastest way to confirm you locked the value where you meant to. Run the --lock-env form once per environment as part of provisioning, with the real key for that environment. The deprecated -l and --lock behave like --lock-env but should not be used in new scripts.
# Longhand.
$ bin/magento config:set --lock-env loyalty/api/key abc123
Value was saved in app/etc/env.php and locked.
# Shorthand. -e is the same option as --lock-env.
$ bin/magento config:set -e loyalty/api/key abc123
Value was saved in app/etc/env.php and locked.
# -c is the same option as --lock-config: shared, committed.
$ bin/magento config:set -c loyalty/api/endpoint https://api.example.com
Value was saved in app/etc/config.php and locked.
# Without a lock option the value goes to the database instead.
$ bin/magento config:set loyalty/general/points_per_currency 25
Value was saved.What lands in env.php
The system key mirrors the config path tree, one level down from the scope: default for installation-wide values, websites or stores for scoped ones. A value present here is the one Magento uses and the admin field is greyed out. Note the plain text: this is exactly why the file is excluded from source control and why the Encrypted backend model protects the database copy, not this one.
<?php
// app/etc/env.php - per environment, never committed
return [
'system' => [
'default' => [
'loyalty' => [
'api' => [
'key' => 'abc123',
'endpoint' => 'https://api.example.com',
],
],
],
],
// db, crypt, cache and other deployment keys also live here
];
Discussion