> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ctrlplane.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Environments

> Define where to deploy — logical stages that group resources

An **Environment** defines **where** to deploy. It represents a logical
deployment stage in your pipeline, such as development, staging, or production.
Environments use selectors to dynamically determine which resources belong to
them — answering the question of *where* a version should be released.

<Tip>
  In Ctrlplane's mental model: [**Deployments**](./deployments) = what & how, **Environments** = where, [**Policies**](../policies/overview) = when.
</Tip>

## What is an Environment?

Environments organize your deployment pipeline into stages:

* **Development** - Where developers test their changes
* **QA** - Quality assurance and testing
* **Staging** - Pre-production validation
* **Production** - Live customer-facing environment

Environments are **not** static groups of resources. Instead, they use **selectors** to dynamically match resources based on their metadata. This means:

* New resources matching the selector automatically join the environment
* Resources can belong to multiple environments
* No manual resource assignment needed

## Environment Properties

```yaml theme={null}
id: env_abc123
systemId: sys_xyz789
name: Production
directory: environments/prod
description: Production environment for customer-facing services
resourceSelector: resource.metadata["environment"] == "production"
createdAt: "2024-01-15T10:00:00Z"
metadata:
  owner: platform-team
  alert-channel: "#prod-alerts"
```

### name

Display name for the environment.

**Examples**:

* "Production"
* "Staging"
* "Development"
* "QA US East"

### systemId

The system this environment belongs to. Environments are scoped to systems.

### resourceSelector

The selector that determines which resources belong to this environment. This is the heart of how environments work.

**Example Selectors**:

**Simple metadata match** (CEL expression):

```yaml theme={null}
resourceSelector: resource.metadata["environment"] == "production"
```

**Multiple conditions (AND)**:

```yaml theme={null}
resourceSelector: >-
  resource.metadata["environment"] == "production" &&
  resource.metadata["region"] == "us-east-1"
```

**OR logic**:

```yaml theme={null}
resourceSelector: >-
  resource.metadata["region"] == "us-east-1" ||
  resource.metadata["region"] == "us-west-2"
```

### directory

Optional path for hierarchical organization of environments.

**Examples**:

* `""` - Root level
* `"regions"` - First level grouping
* `"regions/us-east"` - Nested grouping

**Use Cases**:

* Organizing by region: `regions/us-east`, `regions/eu-west`
* Organizing by type: `permanent/production`, `temporary/feature-1`
* Organizing by team: `teams/platform`, `teams/product`

### description

Optional description of the environment's purpose and usage.

### metadata

Key-value pairs with environment information (not used for resource matching).

**Common Metadata**:

```yaml theme={null}
metadata:
  owner: platform-team
  alert-channel: "#prod-alerts"
  cost-center: engineering
  sla: "99.99%"
```

## Creating Environments

### Via Web UI

1. Navigate to your system
2. Click "Environments" tab
3. Click "Create Environment"
4. Fill in:
   * Name: "Production"
   * Description: "Production environment"
   * Resource Selector: Add conditions
5. Click "Create"

### Via CLI

```yaml theme={null}
# environment.yaml
type: Environment
name: Production
description: Production environment for customer-facing services
resourceSelector: resource.metadata["environment"] == "production"
metadata:
  owner: platform-team
```

```bash theme={null}
ctrlc apply -f environment.yaml
```

### Multiple Environments

```yaml theme={null}
# environments.yaml
---
type: Environment
name: Development
description: Development environment
resourceSelector: resource.metadata["environment"] == "development"
---
type: Environment
name: Staging
description: Staging environment
resourceSelector: resource.metadata["environment"] == "staging"
---
type: Environment
name: Production
description: Production environment
resourceSelector: resource.metadata["environment"] == "production"
```

```bash theme={null}
ctrlc apply -f environments.yaml
```

## How Resource Selectors Work

Selectors match resources based on their metadata, kind, identifier, or other properties.

### Selector Types

Ctrlplane uses CEL (Common Expression Language) for resource selectors.

#### Metadata Selector

Match resources by metadata key-value pairs.

**Single condition**:

```yaml theme={null}
resourceSelector: resource.metadata["environment"] == "production"
```

**Operators**:

* `==` - Exact match
* `!=` - Not equals
* `in` - Value in list
* `has()` - Key exists

**Multiple conditions (AND)**:

```yaml theme={null}
resourceSelector: >-
  resource.metadata["environment"] == "production" &&
  resource.metadata["region"] == "us-east-1"
```

**Multiple conditions (OR)**:

```yaml theme={null}
resourceSelector: >-
  resource.metadata["region"] == "us-east-1" ||
  resource.metadata["region"] == "us-west-2"
```

#### Kind Selector

Match resources by their `kind` field.

```yaml theme={null}
resourceSelector: resource.kind == "KubernetesCluster"
```

#### Identifier Selector

Match resources by their identifier.

```yaml theme={null}
resourceSelector: resource.identifier.contains("prod")
```

#### Complex CEL Expressions

Use CEL for complex matching logic.

```yaml theme={null}
resourceSelector: >-
  resource.metadata["environment"] == "production" &&
  resource.metadata["tier"] == "critical"
```

### Selector Evaluation

When determining which resources belong to an environment:

1. Ctrlplane evaluates the `resourceSelector` against all resources
2. Resources that match are included in the environment
3. As resources are added/updated, they're re-evaluated
4. Resources are automatically added/removed as they match/unmatch

**Example**:

Environment selector:

```yaml theme={null}
resourceSelector: resource.metadata["environment"] == "production"
```

Resources:

* ✅ `{name: "Cluster A", metadata: {environment: "production"}}` - **Matched**
* ❌ `{name: "Cluster B", metadata: {environment: "staging"}}` - Not matched
* ✅ `{name: "Cluster C", metadata: {environment: "production", region: "us-east"}}` - **Matched**

Result: Environment contains Cluster A and Cluster C

## Release Targets

When you create a deployment and an environment, Ctrlplane automatically creates **release targets** for each matching resource.

**Formula**: `Deployment × Environment × Resource = Release Target`

**Example**:

Given:

* Deployment: "API Service"
* Environment: "Production" (matches 3 resources)
  * Resource A: k8s-prod-use1
  * Resource B: k8s-prod-usw2
  * Resource C: k8s-prod-euw1

Result: 3 release targets created:

1. API Service → Production → k8s-prod-use1
2. API Service → Production → k8s-prod-usw2
3. API Service → Production → k8s-prod-euw1

When you create a new deployment version, jobs can be created for each release target (subject to policies).

## Hierarchical Environments

Use the `directory` field to organize environments hierarchically.

### Example Structure

```
Root
├── regions/
│   ├── us-east-1/
│   │   ├── Production
│   │   └── Staging
│   └── eu-west-1/
│       ├── Production
│       └── Staging
└── temporary/
    ├── feature-branch-1
    └── feature-branch-2
```

### Creating Hierarchical Environments

```yaml theme={null}
# hierarchical-environments.yaml
---
type: Environment
name: Global Production
directory: ""
resourceSelector: resource.metadata["environment"] == "production"
---
type: Environment
name: US East Production
directory: regions/us-east
resourceSelector: >-
  resource.metadata["environment"] == "production" &&
  resource.metadata["region"] == "us-east-1"
---
type: Environment
name: US East 1a Production
directory: regions/us-east/availability-zones
resourceSelector: >-
  resource.metadata["environment"] == "production" &&
  resource.metadata["zone"] == "us-east-1a"
```

```bash theme={null}
ctrlc apply -f hierarchical-environments.yaml
```

### Benefits

* **Visual organization** in UI
* **Easier navigation** with many environments
* **Logical grouping** by region, team, or purpose
* **Permission scoping** (future feature)

## Common Environment Patterns

### Standard Pipeline

```yaml theme={null}
# environments.yaml
---
type: Environment
name: Development
resourceSelector: resource.metadata["environment"] == "development"
---
type: Environment
name: Staging
resourceSelector: resource.metadata["environment"] == "staging"
---
type: Environment
name: Production
resourceSelector: resource.metadata["environment"] == "production"
```

```bash theme={null}
ctrlc apply -f environments.yaml
```

### Multi-Region Environments

```yaml theme={null}
# multi-region-environments.yaml
---
type: Environment
name: Production US East
directory: production/us-east
resourceSelector: >-
  resource.metadata["environment"] == "production" &&
  resource.metadata["region"] == "us-east-1"
---
type: Environment
name: Production US West
directory: production/us-west
resourceSelector: >-
  resource.metadata["environment"] == "production" &&
  resource.metadata["region"] == "us-west-2"
```

```bash theme={null}
ctrlc apply -f multi-region-environments.yaml
```

### Canary Environments

```yaml theme={null}
# canary-environments.yaml
---
type: Environment
name: Production Canary
resourceSelector: >-
  resource.metadata["environment"] == "production" &&
  resource.metadata["canary"] == "true"
---
type: Environment
name: Production Stable
resourceSelector: >-
  resource.metadata["environment"] == "production" &&
  resource.metadata["canary"] != "true"
```

```bash theme={null}
ctrlc apply -f canary-environments.yaml
```

### Team-Specific Environments

```yaml theme={null}
# team-environments.yaml
---
type: Environment
name: Platform Team Development
resourceSelector: >-
  resource.metadata["team"] == "platform" &&
  resource.metadata["environment"] == "development"
---
type: Environment
name: Product Team Development
resourceSelector: >-
  resource.metadata["team"] == "product" &&
  resource.metadata["environment"] == "development"
```

```bash theme={null}
ctrlc apply -f team-environments.yaml
```

## Viewing Environment Resources

### Via CLI

```bash theme={null}
ctrlc api get resources --environment {environmentId}
```

Returns all resources matching the environment's selector.

### Via Web UI

1. Navigate to the environment
2. Click "Resources" tab
3. See all matched resources

The UI shows:

* Resource name and kind
* Metadata
* Current deployed versions
* Job status

## Environment Variables

Environments can have variables that differ per deployment.

### Deployment Variables

Define a variable at the deployment level, then set values per environment. Variables are typically configured via the Web UI, but can also be managed via API.

See the [Deployments documentation](./deployments) for details on creating and setting deployment variables.

During job execution, the appropriate value is resolved based on the environment.

## Policies and Environments

Policies often target specific environments using selectors.

### Approval Required for Production

```yaml theme={null}
# policy.yaml
type: Policy
name: Production Approval Required
selectors:
  - environments: environment.name == "Production"
rules:
  - anyApproval:
      minApprovals: 2
```

```bash theme={null}
ctrlc apply -f policy.yaml
```

### Environment Progression

Ensure deployments go to staging before production:

```yaml theme={null}
# progression-policy.yaml
type: Policy
name: Staging Before Production
selectors:
  - environments: environment.name == "Production"
rules:
  - environmentProgression:
      fromEnvironment: Staging
      toEnvironment: Production
```

```bash theme={null}
ctrlc apply -f progression-policy.yaml
```

## Best Practices

### Naming

**Good Names**:

* ✅ "Production"
* ✅ "Staging"
* ✅ "Development"
* ✅ "QA US East"

**Avoid**:

* ❌ "Prod" (use full names)
* ❌ "Environment 1" (not descriptive)
* ❌ "John's Test" (not permanent)

### Selector Design

**Do**:

* ✅ Use consistent metadata keys across resources
* ✅ Make selectors explicit and clear
* ✅ Test selectors match expected resources
* ✅ Document complex selectors

**Don't**:

* ❌ Overly complex selectors that are hard to understand
* ❌ Selectors that might accidentally match wrong resources
* ❌ Selectors based on frequently changing metadata

### Environment Count

**Start Simple**:

```
- Development
- Production
```

**Grow as Needed**:

```
- Development
- Staging
- Production
- Production Canary
```

**Don't Over-Engineer**:

* Avoid creating environments you won't use
* Consolidate similar environments
* Use resource metadata to differentiate within an environment

## Troubleshooting

### Environment shows no resources

* Check the resource selector syntax
* Verify resources exist with matching metadata
* Test selector with "Query Resources" feature
* Check resources aren't deleted

### Wrong resources in environment

* Review resource selector logic
* Check for OR vs AND conditions
* Verify resource metadata is correct
* Test selector in isolation

### Release targets not created

* Verify environment has matching resources
* Check deployment has resource selector (if any)
* Ensure deployment and environment are in same system
* Review logs for errors

## Next Steps

* [Deployments](./deployments) - Create deployments
* [Selectors](./selectors) - Deep dive into selector syntax
* [Release Targets](./release-targets) - Understand the deployment matrix
* [Policies](../policies/overview) - Control deployments with policies
