# Per-environment settings

**Goal:** make one codebase behave correctly on every [environment](/start-here/glossary/#environment): the right values per environment, secrets handled safely, and PHP tuned where the platform allows it.

A Cloud Platform application runs the same codebase on every environment, so anything that must differ between Dev, Stage, and Prod has to come from outside the code. There are three surfaces for that: environment variables, the environment's PHP settings, and conditional logic in `settings.php`.

## What you'll have when you're done

- `settings.php` logic that branches on the platform's own environment variables.
- Custom variables for your per-environment values, with secrets kept out of them.
- PHP settings tuned per environment where the platform allows it, defaults left alone elsewhere.

## Prerequisites

- A Cloud Platform application and interface access (variables and PHP settings are set there)
- [Ship code to environments](/cloud-platform/code-workflow/quickstart/), since `settings.php` changes deploy like any code change

## Steps

<Steps>

1. ### Branch on the platform's environment variables

   Every environment exposes variables your code and drush commands can read. The ones you'll actually branch on:

   | Variable | What it holds |
   |---|---|
   | `AH_SITE_ENVIRONMENT` | The environment name: `dev`, `test`, `prod`, or `ide`. The environment labeled **Stage** is named `test`, so that is the value to compare against |
   | `AH_PRODUCTION` / `AH_NON_PRODUCTION` | `1` on the matching side, undefined on the other |
   | `AH_SITE_NAME` | Site group + environment (used to build absolute paths, e.g. in [scheduled jobs](/cloud-platform/configure/cron/)) |
   | `AH_SITE_GROUP` | The site group / Unix user name |
   | `AH_GIT_REF` / `AH_GIT_SHA` | The deployed branch/tag name and exact commit hash, for logs and error trackers |
   | `TEMP` | The environment's real temp directory (`/mnt/tmp/[site].[env]`); use it instead of `/tmp`, which is small and fills |

   The full table is in [Using environment variables](https://docs.acquia.com/acquia-cloud-platform/using-environment-variables) on docs.acquia.com. Typical use in `settings.php`:

   ```php
   if (getenv('AH_SITE_ENVIRONMENT') === 'prod') {
     $config['system.performance']['cache']['page']['max_age'] = 43200;
   }
   ```

   One ordering rule: Acquia injects platform configuration into `settings.php` via its own include (the [Acquia require line](https://docs.acquia.com/acquia-cloud-platform/acquia-require-line)). Environment-variable logic goes *after* that include, or it can be clobbered by it.

2. ### Define custom variables for your own values

   For values you define yourself (an API URL, a feature flag), add custom environment variables per environment with acli:

   ```bash
   acli api:environments:variable-create myapp.dev --name='MY_VARIABLE' --value='the-value' --task-wait
   ```

   `variable-list`, `variable-update`, and `variable-delete` cover the rest of the lifecycle, each taking the same environment ID or alias. Read the value from PHP the way you read a platform one: `getenv('MY_VARIABLE')`. The Cloud UI has the same actions on the environment; see [Creating custom environment variables](https://docs.acquia.com/acquia-cloud-platform/creating-custom-environment-variables) on docs.acquia.com.

   Rules and quirks worth knowing before you rely on them:

   - Names can't start with `AH` or `ACQUIA`; no spaces, tabs, or quotes in names or values; 255-byte names, 5000-byte values, 100 KB total.
   - **They are not a secrets store.** Custom variables are unencrypted and can leak into Drupal watchdog logs. Acquia's own guidance: don't put SSL certificates, SSH keys, or login/API credentials in them. For secrets, use a file in the private file system read from `settings.php`, and follow the credential-handling patterns in the [auth guide](/source-cms/authenticate/guide/).

   Prefer a custom variable over committed config when the value genuinely differs per environment or shouldn't live in git history; prefer committed configuration for everything else, so environments stay reproducible from the repository. When whole modules or configuration objects differ per environment rather than single values, that's a job for a configuration split instead: see [manage configuration](/cloud-platform/code-workflow/configuration/#steps).

3. ### Tune PHP settings where the platform allows

   `acli api:environments:update` sets these per environment, one flag each, queuing a task that `--task-wait` blocks on:

   ```bash
   acli api:environments:update myapp.dev --memory_limit='192' --task-wait
   ```

   - **PHP version** (`--lang_version`) per environment. Update Dev or Stage first, verify there, then Prod.
   - **PHP memory limit** per process (`--memory_limit`, default 128 MB). Note the split that surprises people: drush and other CLI commands get 512 MB regardless, which is why a memory-starved cron job may succeed via drush and fail via the web. See [scheduled jobs](/cloud-platform/configure/cron/).
   - **`max_execution_time`** (`--max_execution_time`, default 300 seconds). Longer isn't better: a request that needs more than a couple of minutes should become a [scheduled job](/cloud-platform/configure/cron/); on [Cloud Next](/start-here/glossary/#cloud-next), web requests past 10 minutes can be interrupted by routine platform maintenance.
   - **OPcache size, APCu size, interned-strings buffer, max input vars, max POST size, memcached memory** (`--opcache`, `--apcu`, `--interned_strings_buffer`, `--max_input_vars`, `--max_post_size`, `--memcached_limit`): advanced dials; leave defaults unless Acquia Support advises otherwise or you have measurements. Some upper limits depend on subscription tier; see [Configuring PHP settings](https://docs.acquia.com/acquia-cloud-platform/configuring-php-settings) on docs.acquia.com.

   What you *can't* tune: anything at the MySQL server level (centrally managed), Varnish behavior (steered by [response headers](/cloud-platform/caching/guide/), not settings), and the web-server configuration itself.

4. ### Override `settings.php`, including the database

   Acquia injects database credentials through its own include; your `settings.php` never hardcodes them, and after the include runs, `$databases` is populated. To *adjust* the connection rather than define it, modify `$databases` after the include. The supported example: MySQL server variables can't be changed globally, but can be set per session via connection `init_commands`:

   ```php
   // After the Acquia require line.
   $databases['default']['default']['init_commands'] = [
     'isolation' => 'SET SESSION transaction_isolation="READ-COMMITTED"',
   ];
   ```

   The same post-include block is where per-environment overrides of any Drupal setting live, guarded by `AH_SITE_ENVIRONMENT` checks as above. The full set of worked `$databases` cases (external databases, multisite variants) is in [Overriding Drupal $databases settings](https://docs.acquia.com/acquia-cloud-platform/overriding-drupal-databases-settings) on docs.acquia.com.

</Steps>

## When something goes wrong

**A changed variable isn't visible**: existing SSH sessions don't see updates (reconnect), and changing a variable re-provisions the environment's pods, which takes a moment.

**An override has no effect**: check its position relative to the Acquia require line; platform injection runs in that include, and logic placed before it gets overwritten.

**Works via drush, fails via the web (or vice versa)**: remember the memory-limit split in step 3.

## Next steps

- [Run scheduled jobs](/cloud-platform/configure/cron/): the other half of environment configuration, recurring work.
- [Manage configuration](/cloud-platform/code-workflow/configuration/): the Drupal configuration that travels in code, and how `settings.php` overrides switch it per environment.
- [How caching works](/cloud-platform/caching/guide/): why cache lifetime is a header concern, not a PHP setting.
- [Diagnose problems](/cloud-platform/observability/diagnose/): when a setting change is a guess, measure instead.
