Skip to main content

Crossplane Ep 7: Environment Configs

Rachmat Hidayat
Author
Rachmat Hidayat
Learn & sharing insights on TypeScript, Go, Kubernetes, DevOps, DevSecOps, SRE, Platform Engineering, AI/ML Engineering, and MLOps.
crossplane - This article is part of a series.
Part 7: This Article
If you are hardcoding vpcId: vpc-12345 inside your Composition YAML, you are building a fragile system. If you deploy that same Composition to your Staging cluster (which has a different VPC), every database provisioning attempt will fail. To build a robust IDP, Compositions must be context-aware. Enter EnvironmentConfigs.

1. The Need for Global Variables
#

In Episode 6, we used FromCompositeFieldPath to read data from the user’s Claim (e.g., storageGB).

But an Application Developer shouldn’t be forced to include vpcId: vpc-12345 or region: us-east-1 in their Claim. They don’t know the VPC ID, and they shouldn’t care.

That information is a Global Variable specific to the environment (the cluster) that Crossplane is running in.

We need a way to store global variables in the cluster, and have the Composition pull those variables in at runtime.


2. Defining an EnvironmentConfig
#

Crossplane provides a built-in Custom Resource called EnvironmentConfig. It is essentially a global JSON dictionary.

Let’s assume this cluster is our European Production cluster. We will create a configuration specific to this environment.

Create a file named cluster-config.yaml:

apiVersion: apiextensions.crossplane.io/v1alpha1
kind: EnvironmentConfig
metadata:
  # We can create multiple configs and select them via labels
  name: eu-prod-config
  labels:
    environment: production
data:
  # This is a free-form dictionary. You can put anything here!
  aws:
    region: eu-central-1
    vpcId: vpc-99998888
    subnetGroup: my-prod-subnets
  defaultTags:
    managedBy: crossplane
    costCenter: "815"

Apply it to the cluster:

kubectl apply -f cluster-config.yaml

This data is now floating in the cluster, waiting to be consumed.


3. Consuming Environment Data in Compositions
#

To use this data in a Composition, we must do two things:

  1. Tell the Composition which EnvironmentConfig to load.
  2. Write an Environment patch to inject the data into the Managed Resources.

Step 1: Loading the Environment
#

Open the Composition we wrote in Episode 6 (composition-postgres.yaml).

At the very top of the spec (above resources), add the environment block. We use a Label Selector to dynamically find the correct config!

spec:
  compositeTypeRef:
    apiVersion: database.acmecorp.com/v1alpha1
    kind: XPostgreSQLInstance

  # 1. Instruct Crossplane to load the global variables
  environment:
    environmentConfigs:
      - type: Selector
        selector:
          matchLabels:
            # It will dynamically find our 'eu-prod-config'!
            environment: production

Step 2: The Environment Patch
#

Now, inside the resources block for the RDS instance, we add a new Patch. Notice the type is FromEnvironmentFieldPath.

      patches:
        # Patch the Region
        - type: FromEnvironmentFieldPath
          # Read from the EnvironmentConfig Data dictionary
          fromFieldPath: aws.region
          # Write to the AWS MR
          toFieldPath: spec.forProvider.region
          
        # Patch the Cost Center Tag
        - type: FromEnvironmentFieldPath
          fromFieldPath: defaultTags.costCenter
          toFieldPath: spec.forProvider.tags.CostCenter

The Magic of Portability
#

Now, your Composition is 100% portable.

  • If you kubectl apply this Composition in the European cluster, the databases will boot in eu-central-1.
  • If you apply the exact same Composition file to the American cluster (which has an EnvironmentConfig defining aws.region: us-east-1), the databases boot in America.

You have successfully achieved the DRY (Don’t Repeat Yourself) principle in Platform Engineering.


4. Advanced: Resolution Policies (Optional vs Required)
#

When you rely on EnvironmentConfigs, what happens if a platform engineer forgets to create the EnvironmentConfig object before an Application Developer submits a Claim?

By default, the FromEnvironmentFieldPath patch is Required. If Crossplane evaluates the patch and cannot find aws.region in the environment, the entire Composition engine halts. The XR will throw an error event, and no AWS resources will be created.

This is usually the desired behavior (fail safe, rather than deploying to a default region accidentally).

However, if you want a patch to be optional (e.g., injecting a specific debugging flag that only exists in development clusters), you can change the policy:

        - type: FromEnvironmentFieldPath
          fromFieldPath: debugging.logLevel
          toFieldPath: spec.forProvider.logLevel
          policy:
            fromFieldPath: Optional # Do not crash if missing!

Troubleshooting & Common Errors
#

  1. cannot resolve environment config: selector did not match any config

    • Root Cause: The matchLabels in your Composition’s environmentConfigs block does not match the labels on any EnvironmentConfig currently running in the cluster.
    • Solution: Ensure the EnvironmentConfig is applied, and verify the spelling of the labels.
  2. cannot apply patch: fromFieldPath not found in environment

    • Root Cause: The EnvironmentConfig was found, but the specific JSON path (e.g., aws.vpcId) you requested in your patch does not exist in the data block.
    • Solution: Double check the YAML indentation of your cluster-config.yaml.

Conclusion & Next Steps
#

EnvironmentConfigs act as the global state for your Internal Developer Platform. By injecting VPCs, Regions, and Organizational Tags dynamically, your Compositions become highly reusable assets that can be distributed across dozens of Kubernetes clusters.

But what if a physical dependency isn’t static? What if your Composition needs to create a brand new VPC, and then create an RDS Database inside that VPC? How do you ensure the VPC is created first, and how do you pass its dynamic ID to the Database?

In Episode 8: Managing Dependencies Between Resources, we will learn how to orchestrate multi-resource deployments and safely pass variables between them using the Pipeline.

crossplane - This article is part of a series.
Part 7: This Article