---
url: /getting-started/environments.md
---
# Environments

Since v2.6, Evolve treats deployment environments as first class citizens. A project declares its environments in `config/evolve.php`, each with a name, a type that drives policy, and a target directory on the server.

```php [config/evolve.php]
'deployment' => [
    'default' => env('DEPLOYMENT_DEFAULT', 'live'),

    'environments' => [
        'live'    => ['type' => 'live',    'target' => 'www',     'url' => 'https://client.ch'],
        'staging' => ['type' => 'staging', 'target' => 'staging', 'url' => 'https://staging.client.ch'],
    ],
    ...
],
```

The name is what you type on the CLI (`php artisan deploy staging`). The target is the physical directory under the server path and may keep its historical name. Development is the fourth type; it is your local machine and never declared as a remote.

Projects that still use the legacy `target` and `allowed-targets` keys keep working unchanged. The default target behaves as live and the allowed targets behave as staging.

## Types and policies

Three types: `development`, `staging`, `live`. The type determines what the tooling allows and how the running site behaves. The policy lives in the package and is deliberately not configurable per project; single `.env` variables override single defaults, see [Environment Variables](/getting-started/environment-variables). `testing` is still accepted in declarations and `.env` files written before 2.8 and means staging.

| | development | staging | live |
|---|---|---|---|
| Meant for | the local machine, or an early alpha on a server | every remote test site | production |
| `APP_ENV` on the server | `local` | `staging` | `production` |
| Debug (`APP_DEBUG` default) | on | on | off, `evolve:env:verify` fails otherwise |
| Outgoing mail | as configured (Mailpit, `log`) | redirected to `EVOLVE_MAIL_REDIRECT` until a developer switches it to real | real recipients |
| Frontend banner | shown | shown | none |
| Preview password | never | as soon as `EVOLVE_PROTECT_PASSWORD` is set | never |
| Search engines | indexable | noindex | indexable |
| Page cache | `APP_PAGE_CACHE`, writes on `production` and `staging` only | same | same |
| Deploy confirmation | yes/no, `--force` skips | yes/no, `--force` skips | type the environment name, `--force` is ignored |
| Branch check | any branch | any branch | warns off master, typed confirm |
| Database pushes into it | confirm plus backup plus sanitize | same | never |

A staging site starts safe: mail is caught, the site is noindexed, and it is password protected the moment a password is set. The frontend banner is a small pill at the bottom centre naming the environment; a visitor can shrink it to a dot in the corner, and the browser remembers that choice. What it does beyond that is the developer's call, per site, in the Developers tab: switch mail to real recipients for a dress rehearsal, hide the banner, turn debug off. Nothing has to be declared twice.

A deployed test site runs `APP_ENV=staging`, never `testing`: Laravel interprets an app environment called `testing` as running unit tests, which changes framework behavior. Development can be declared as a remote environment too (an early alpha on a server); it then runs `APP_ENV=local` with the banner and without any mail redirect.

The runtime side is driven by `EVOLVE_ENV_TYPE` in the server `.env`, which `evolve:env:setup` writes automatically. Note that the preview protection and the page cache are mutually exclusive: cached pages are served without PHP, so while a protect password is set the cache does not write.

## Commands

```bash
# List declared environments
php artisan evolve:env:list

# Bootstrap a new environment: remote directories, server .env, database, media
php artisan evolve:env:setup staging --from=live --db=copy:live --media-from=live --deploy

# Verify an environment against its declaration
php artisan evolve:env:verify staging
php artisan evolve:env:verify --all

# Compare .env key names across .env.example, local .env and remote environments
php artisan evolve:env:diff

# Copy a database between environments, entirely on the server (never into live)
php artisan evolve:db:copy live staging

# Open an SSH session in an environment
php artisan evolve:ssh staging
```

`evolve:env:setup` needs three things prepared on the hosting side first: a vhost pointing at `<target>/current/public`, a DNS record, and the MySQL database. Everything else, including the first deployment, is handled by the command.

`evolve:db:copy` always writes a compressed backup of the destination database next to the target's `releases/` directory before importing, and then runs the SQL statements configured under `deployment.database.sanitize`, which is the place to anonymize personal data on non live copies.

The destination ends up with exactly the tables of the source. The command first dumps the source into a file, then drops every table and view in the destination, and only then imports. Tables the source lacks (leftovers of earlier deploys on the destination) are gone afterwards, so the next `migrate` does not trip over them. If the source dump fails, the destination stays untouched. Before 2.22.3 the copy kept such leftover tables.

## Migrating an existing project

1. Update the package. Nothing breaks; the legacy config keeps working.
2. Declare the `environments` map in `config/evolve.php`, keeping the historical directory names as targets.
3. Run `php artisan evolve:env:verify --all`. It reports exactly what each server `.env` is missing, typically `EVOLVE_ENV_TYPE` and the switch of test sites from `APP_ENV=production` to `staging`.
4. Fix the server `.env` files. Switching a test site to `staging` activates the noindex header, the mail redirect and, once a password is set, the basic auth gate. That is the point.

## The environment in the settings panel

The Developers tab of the settings panel shows the environment. What a user can do there depends on the role:

* A **developer** sees the technical cards: the environment type (which writes `APP_ENV` and `EVOLVE_ENV_TYPE` together, so they cannot drift), debug, the frontend banner, mail safety with the redirect address, the preview user and password, and one switch per module.
* An **admin** sees the environment read only. Technical settings are a developer concern, and the server side drops those keys from an admin's save whatever the form sent.

The panel edits the `.env` of the instance it runs on; each environment keeps its own. The deployment writes the posture keys from the declaration in `config/evolve.php` when it bootstraps an environment, and `evolve:env:verify` compares the server `.env` against that declaration. The environment consistency health check does the same at runtime: it fails when `APP_ENV` and `EVOLVE_ENV_TYPE` contradict each other and warns on a remote without a declared type.

When `EVOLVE_ENV_TYPE` is not set at all, the type is inferred from `APP_ENV`: `production` means live, `staging` means staging, anything else means development. That is the state of a project that has not run the environment setup yet.

How mail behaves per environment is described on the [Mail](/getting-started/mail) page; roles on [Roles](/getting-started/roles).
