Who this is for. The developer wiring an integration, a payment provider or a mail setting who needs staging and production to behave differently. Afterwards you know which values can differ per environment, who controls each, and where a secret may and may not live.
The environments of a site share one database (what environments share), so most configuration is the same on all of them. Knowing the few places where it can differ saves a lot of "it works on staging".
Where configuration lives
| What | Stored | Differs per environment? | Who changes it |
|---|---|---|---|
| Module settings: cache, mail, search, commerce and the rest | Database, saved in the admin | No. One value for the whole account: every site and every environment | Administrators |
| Setting overrides | .settings.json next to the environment's code, outside git | Yes, and it wins over the admin | Core dna |
| Mode and service connections | .environment.json in the environment's folder, or the platform's default | Yes | Core dna |
| Site data: keys, URLs and switches your templates read | Database: a value for one site, or an account-wide value every site falls back to | No | Administrators |
| Payment methods and their keys | Database, each method for one site or for every site of the account | No | Administrators |
| Tax providers and tax zones | Database, per site | No | Administrators |
| The theme's own build settings | The repository | Only by branch | Developers |
A setting's value is read in this order, each step overriding the last: the module's default, the value saved in the admin, the signed-in administrator's own value where the setting allows one, and finally .settings.json. The file wins by design, so that an administrator saving a settings page cannot undo an infrastructure decision. It is how one environment gets a different value for a single setting; ask Core dna for the change, because it is not in the repository and a theme cannot set it.
Because module settings are account-wide, a setting changed for one site changes it for every site on the account. Check which sites share the account before you change one.
Where secrets belong
- Never in the repository, in any branch, for any environment. A key committed once stays in the history; deleting it later does not take it back, and the only fix is to replace the key.
- Integration keys in site data, read by the template or job that needs them, so the code is identical everywhere and the value is set where the site runs.
- Provider keys on their records: a payment method's keys are entered on the payment method, a tax provider's on the provider.
- Never printed into a page. A key a template reads to build a server-side request must not reach the HTML or a script the browser loads. When the browser must call a service, it needs a key made for browsers, such as a provider's publishable key.
- API credentials for your own integrations belong in the integration's secret store, not in site data the site's templates can read. Prefer an OAuth client; an API key is the older option (Authentication and authorisation).
A sandbox credential on staging
Site data is shared by all of a site's environments, so staging reads production's integration keys unless you arrange otherwise. When staging must talk to a partner's sandbox, store both values under two keys and choose by environment: