Skip to main content
An Environment 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. From a deployment perspective, an environment is additional metadata attached to the resources it selects.

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

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):
resourceSelector: resource.metadata["environment"] == "production"
Multiple conditions (AND):
resourceSelector: >-
  resource.metadata["environment"] == "production" &&
  resource.metadata["region"] == "us-east-1"
OR logic:
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:
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

# environment.yaml
type: Environment
name: Production
description: Production environment for customer-facing services
resourceSelector: resource.metadata["environment"] == "production"
metadata:
  owner: platform-team
ctrlc apply -f environment.yaml

Multiple Environments

# 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"
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:
resourceSelector: resource.metadata["environment"] == "production"
Operators:
  • == - Exact match
  • != - Not equals
  • in - Value in list
  • has() - Key exists
Multiple conditions (AND):
resourceSelector: >-
  resource.metadata["environment"] == "production" &&
  resource.metadata["region"] == "us-east-1"
Multiple conditions (OR):
resourceSelector: >-
  resource.metadata["region"] == "us-east-1" ||
  resource.metadata["region"] == "us-west-2"

Kind Selector

Match resources by their kind field.
resourceSelector: resource.kind == "KubernetesCluster"

Identifier Selector

Match resources by their identifier.
resourceSelector: resource.identifier.contains("prod")

Complex CEL Expressions

Use CEL for complex matching logic.
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:
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

# 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"
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

# 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"
ctrlc apply -f environments.yaml

Multi-Region Environments

# 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"
ctrlc apply -f multi-region-environments.yaml

Canary Environments

# 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"
ctrlc apply -f canary-environments.yaml

Team-Specific Environments

# 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"
ctrlc apply -f team-environments.yaml

Viewing Environment Resources

Via CLI

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 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

# policy.yaml
type: Policy
name: Production Approval Required
selectors:
  - environments: environment.name == "Production"
rules:
  - anyApproval:
      minApprovals: 2
ctrlc apply -f policy.yaml

Environment Progression

Ensure deployments go to staging before production:
# progression-policy.yaml
type: Policy
name: Staging Before Production
selectors:
  - environments: environment.name == "Production"
rules:
  - environmentProgression:
      fromEnvironment: Staging
      toEnvironment: Production
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