Orchestrating Ephemeral Developer Environments via Backstage and Crossplane
In the modern cloud-native landscape, the “inner loop” of software development often hits a significant bottleneck: environment contention. Traditional static staging environments are frequently “snowflakes”—brittle, inconsistently configured, and prone to drift. As organizations scale, the manual provisioning of cloud resources becomes a ticket-driven nightmare for Platform Engineers and a source of frustration for developers.
The solution lies in Ephemeral Developer Environments (EDEs). These are on-demand, short-lived, and isolated environments that mirror production. However, building these requires more than just a script; it requires a robust Internal Developer Portal (IDP) and a powerful Control Plane. This article explores the technical orchestration of EDEs using Backstage as the interface and Crossplane as the infrastructure execution engine.
Technical Overview: The Brain and the Body
To build a seamless self-service experience, we must decouple the intent (what the developer wants) from the implementation (how the cloud is configured).
Backstage: The Interface (The Brain)
Backstage serves as the entry point. It provides Software Templates (Scaffolding) that abstract away the complexity of YAML manifests. Developers interact with a UI to request an environment, providing only high-level parameters like region, database_size, or expiration_ttl.
Crossplane: The Control Plane (The Body)
Crossplane extends Kubernetes into a Universal Control Plane. Unlike traditional IaC tools (like Terraform) which are execution-based and state-file dependent, Crossplane is API-driven and continuously reconciled. It uses Custom Resource Definitions (CRDs) to represent cloud resources (RDS, S3, EKS).
The Architecture: A GitOps-Mediated Flow
- Request: Developer fills out a Backstage Template.
- Scaffold: Backstage generates a Kubernetes manifest representing a high-level “Composite Resource” (XR).
- Commit: Backstage pushes this manifest to a Git repository.
- Sync: A GitOps controller (ArgoCD or Flux) pulls the manifest into a Kubernetes cluster where Crossplane is running.
- Provision: Crossplane detects the new object, looks up the defined Composition, and calls the Cloud APIs (AWS/GCP/Azure) to provision resources.
- Reconcile: Crossplane continuously monitors the cloud resources, fixing any manual drift automatically.
Implementation Details
1. Defining the Infrastructure Abstraction (Crossplane)
First, the Platform Team defines a CompositeResourceDefinition (XRD). This acts as the “API” for the environment.
# composite-resource-definition.yaml
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
name: xdeveloperenvironments.internal.platform.io
spec:
group: internal.platform.io
names:
kind: XDeveloperEnvironment
plural: xdeveloperenvironments
versions:
- name: v1alpha1
served: true
referenceable: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
dbSize:
type: string
enum: [small, medium, large]
region:
type: string
ttl:
type: integer
description: "Hours until environment is destroyed"
required: [dbSize, region]
2. The Composition (The Blueprint)
The Platform Team then maps this abstract request to actual cloud resources using a Composition.
# composition.yaml
apiVersion: path.crossplane.io/v1
kind: Composition
metadata:
name: aws-dev-env-standard
spec:
compositeTypeRef:
apiVersion: internal.platform.io/v1alpha1
kind: XDeveloperEnvironment
resources:
- name: ephemeral-database
base:
apiVersion: database.aws.upbound.io/v1beta1
kind: Instance
spec:
forProvider:
engine: postgres
instanceClass: db.t3.micro # Map 'small' from XRD here
publiclyAccessible: false
- name: ephemeral-bucket
base:
apiVersion: s3.aws.upbound.io/v1beta1
kind: Bucket
spec:
forProvider:
region: us-east-1
3. Backstage Software Template
The Backstage template provides the UI form that generates the manifest for the XDeveloperEnvironment.
# backstage-template.yaml
apiVersion: scaffold.backstage.io/v1beta3
kind: Template
metadata:
name: create-ephemeral-env
title: New Ephemeral Environment
spec:
parameters:
- title: Environment Details
properties:
envName:
type: string
title: Environment Name
dbSize:
type: string
enum: [small, medium, large]
region:
type: string
default: us-east-1
steps:
- id: fetch-base
name: Fetch Template
action: fetch:template
input:
url: ./content
values:
envName: ${{ parameters.envName }}
dbSize: ${{ parameters.dbSize }}
- id: publish
name: Publish to Git
action: publish:github
input:
allowedHosts: ['github.com']
repoUrl: 'github.com/org/ephemeral-envs'
repoContentsUrl: ${{ steps['fetch-base'].output.remoteUrl }}
Best Practices and Considerations
Time-To-Live (TTL) and Auto-Cleanup
The “ephemeral” nature is key to cost control.
* Implementation: Use a specialized controller or a Kubernetes CronJob that monitors the XDeveloperEnvironment objects. If the creationTimestamp + ttl exceeds the current time, the job deletes the manifest from Git or directly from the cluster.
* Crossplane Deletion Policy: Ensure deletionPolicy: Delete is set in your compositions so that deleting the Kubernetes object triggers the immediate destruction of cloud resources.
Drift Detection and Reconciliation
Unlike Terraform, Crossplane checks the state of the cloud every ~60 seconds. If a developer manually changes an RDS instance size via the AWS Console, Crossplane will revert it to the state defined in the Backstage-generated manifest. This ensures environment consistency.
Security and Governance
- OIDC and ProviderConfigs: Use IAM Roles for Service Accounts (IRSA) in EKS to grant Crossplane permissions. Never hardcode access keys.
- Policy as Code: Use Kyverno or OPA (Open Policy Agent) to validate the
XDeveloperEnvironmentmanifests. For example, you can block any environment request that specifies alargedatabase if the user is not in thesenior-devgroup. - Secret Management: Use External Secrets Operator to sync credentials created by Crossplane (like DB passwords) into the application’s namespace.
Real-World Use Cases
1. AI/ML Experimentation
ML Engineers often need heavy-duty GPU instances (e.g., NVIDIA A100s) for short training bursts. By using Backstage, they can spin up a dedicated EKS node group with specialized hardware, run their training job, and have Crossplane tear it down an hour later, saving thousands in idle cloud costs.
2. Pull Request Previews
Integrate the Backstage API into your CI pipeline. When a PR is opened, the CI triggers the creation of an XDeveloperEnvironment. The resulting cloud URL is posted back to the PR as a comment, allowing reviewers to interact with the live code changes.
Performance Metrics
Organizations adopting this IDP + Control Plane pattern typically see:
* Lead Time Reduction: Environment provisioning time drops from days (ticket-based) to < 10 minutes (self-service).
* Cost Savings: 30–50% reduction in non-production cloud spend due to aggressive TTL enforcement.
* Developer Satisfaction: Significant reduction in cognitive load; developers no longer need to learn HCL (HashiCorp Configuration Language) or navigate cloud consoles.
Conclusion
Orchestrating ephemeral environments via Backstage and Crossplane transforms infrastructure from a static constraint into a dynamic, versioned, and self-healing resource. By shifting to Infrastructure as Data, platform teams can provide “Golden Paths” that empower developers while maintaining strict governance and cost control.
Key Takeaways:
* Backstage is the UI for developer intent.
* Crossplane is the engine that reconciles that intent with reality.
* Abstraction (XRDs) allows platform teams to hide cloud complexity.
* TTL Management is non-negotiable for cost efficiency in ephemeral setups.
By treating the cloud as a programmable extension of the Kubernetes API, organizations can finally realize the promise of true developer self-service.
Discover more from Zechariah's Tech Journal
Subscribe to get the latest posts sent to your email.
