amic, data-driven orchestration. The architecture consists of three components: a Grid Intelligence Client, a Carbon Scheduler, and Instrumented Workloads.
Step-by-Step Technical Implementation
1. Grid Intelligence Integration
Integrate with a provider like WattTime or Electricity Maps to fetch real-time carbon intensity data. This data must be cached to avoid API rate limits and ensure low-latency decision-making.
2. Workload Characterization
Classify workloads by elasticity:
- Hard Real-Time: User-facing requests. Carbon optimization limited to routing or throttling.
- Soft Real-Time: API responses < 500ms. Can tolerate minor latency increases or geo-shifting.
- Elastic/Batch: CI/CD, ML training, data ETL. Can be shifted in time or region freely.
3. Scheduler Logic
Implement a decision engine that compares the current grid intensity against a threshold or forecast. If intensity is high, the scheduler delays execution or routes to a greener region.
Code Examples
TypeScript: Carbon-Aware Scheduler Client
This module encapsulates grid data fetching and decision logic.
import { createClient } from '@watttime/sdk'; // Hypothetical SDK wrapper
import { Cache } from 'cache-manager';
interface CarbonDecision {
shouldExecute: boolean;
recommendedRegion?: string;
delayMs?: number;
intensity: number;
threshold: number;
}
export class CarbonScheduler {
private wattTimeClient: any;
private cache: Cache;
private defaultThreshold: number; // gCO2eq/kWh
constructor(config: { threshold: number; cache: Cache }) {
this.defaultThreshold = config.threshold;
this.cache = config.cache;
this.wattTimeClient = createClient({ token: process.env.WATTTIME_TOKEN });
}
/**
* Fetches current carbon intensity with caching.
* Caches for 5 minutes to balance freshness and API limits.
*/
async getIntensity(region: string): Promise<number> {
const cacheKey = `carbon:${region}`;
const cached = await this.cache.get<number>(cacheKey);
if (cached) return cached;
const data = await this.wattTimeClient.getCurrentIntensity({ region });
await this.cache.set(cacheKey, data.value, 300); // 5 min TTL
return data.value;
}
/**
* Evaluates whether a workload should run now.
*/
async evaluateWorkload(
region: string,
elasticity: 'hard' | 'soft' | 'elastic'
): Promise<CarbonDecision> {
const intensity = await this.getIntensity(region);
// Hard real-time workloads always run; we only report intensity
if (elasticity === 'hard') {
return { shouldExecute: true, intensity, threshold: this.defaultThreshold };
}
// Dynamic threshold based on forecast (simplified logic)
const forecast = await this.getForecast(region);
const isImproving = forecast.nextHour < intensity;
if (intensity > this.defaultThreshold) {
if (elasticity === 'elastic') {
// Delay execution by 30 minutes for batch jobs
return {
shouldExecute: false,
delayMs: 30 * 60 * 1000,
intensity,
threshold: this.defaultThreshold
};
}
if (elasticity === 'soft') {
// Check if another region is greener
const altRegion = await this.findGreenerRegion(region);
if (altRegion && altRegion.intensity < intensity * 0.6) {
return {
shouldExecute: true,
recommendedRegion: altRegion.name,
intensity,
threshold: this.defaultThreshold
};
}
}
}
return { shouldExecute: true, intensity, threshold: this.defaultThreshold };
}
private async getForecast(region: string): Promise<{ nextHour: number }> {
// Implementation depends on API capabilities
return { nextHour: 0 };
}
private async findGreenerRegion(currentRegion: string): Promise<{ name: string; intensity: number } | null> {
const regions = ['us-west-2', 'eu-central-1', 'ap-southeast-1'];
let bestRegion: { name: string; intensity: number } | null = null;
for (const r of regions) {
if (r === currentRegion) continue;
const intensity = await this.getIntensity(r);
if (!bestRegion || intensity < bestRegion.intensity) {
bestRegion = { name: r, intensity };
}
}
return bestRegion;
}
}
Kubernetes Integration Pattern
For production environments, implement a Custom Resource Definition (CRD) and Controller that patches pod schedules based on carbon data.
# carbon-aware-job.yaml
apiVersion: carbon.codcompass.io/v1alpha1
kind: CarbonAwareJob
metadata:
name: data-etl-pipeline
spec:
maxCarbonIntensity: 200 # gCO2eq/kWh
elasticity: elastic
template:
spec:
containers:
- name: etl
image: my-registry/etl:latest
# The controller will delay pod creation or select nodes
# in regions where intensity < maxCarbonIntensity
Architecture Decisions
- Decision at the Edge vs. Center: Scheduler logic should reside in the orchestration layer (e.g., Kubernetes scheduler extension or service mesh sidecar) rather than application code to decouple business logic from sustainability policies.
- Marginal vs. Average Emissions: Always use marginal emissions data for scheduling decisions. Average emissions reflect the historical mix; marginal emissions reflect the impact of the next increment of demand. Shifting load to a region with low marginal emissions ensures you are utilizing spare renewable capacity rather than displacing other users.
- Forecasting: Implement predictive scheduling. If a carbon spike is forecasted in 30 minutes, preemptively queue workloads to avoid the spike.
Pitfall Guide
1. Using Average Emissions for Scheduling
Mistake: Basing decisions on annualized average grid intensity.
Consequence: You may shift workloads to a region that has a low annual average but is currently powered by peaker plants. This increases instantaneous emissions.
Fix: Use real-time marginal intensity data. Marginal data identifies the specific generation source dispatched to meet additional load.
2. Ignoring Data Transfer Emissions
Mistake: Shifting compute to a green region without considering the carbon cost of moving data.
Consequence: If the dataset is large, the network transfer emissions may exceed the compute savings.
Fix: Calculate the "Carbon Breakeven Point." Only shift if Compute_Savings > Transfer_Cost + Latency_Penalty. Implement data locality constraints in the scheduler.
3. Latency Blindness in Soft Real-Time Workloads
Mistake: Applying aggressive geo-shifting to APIs with strict latency SLAs.
Consequence: User experience degrades, leading to churn. Carbon savings are irrelevant if the service is unusable.
Fix: Define strict latency budgets. Geo-shifting should only occur if the green region is within the latency budget (e.g., same continent or edge location).
4. API Rate Limiting and Stale Data
Mistake: Polling grid APIs on every request or caching data for too long.
Consequence: API throttling causes scheduler failures, or stale data leads to suboptimal decisions during rapid grid changes.
Fix: Implement a centralized caching layer with a 5-minute TTL. Use webhooks or server-sent events if the provider supports them.
5. Over-Optimization Overhead
Mistake: Running complex carbon calculation models that consume more energy than they save.
Constance: The carbon-aware scheduler itself becomes a source of emissions.
Fix: Keep decision logic lightweight. Pre-compute thresholds and use simple comparisons in the hot path.
6. Jevons Paradox in Elastic Workloads
Mistake: Making compute cheaper/greener leads to unbounded scaling.
Consequence: Total emissions increase because the organization spins up more instances since "it's green now."
Fix: Implement carbon budgets and caps alongside efficiency measures. Carbon-aware computing must be paired with governance policies.
7. Vendor Lock-in to Single Data Source
Mistake: Hardcoding dependencies on one carbon data provider.
Consequence: If the API goes down or changes pricing, the scheduler breaks.
Fix: Abstract the data source behind an interface. Support fallback providers and allow configuration of multiple data streams.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| ML Model Training | Time Shift + Spot Instances | Training is elastic; spot instances often correlate with high renewable supply. | High Cost Savings (up to 70%) |
| User-Facing API | Geo-Shift within Latency Budget | Maintain SLA while routing to greener regions when possible. | Neutral to Slight Increase |
| Data ETL Pipeline | Time Shift | Batch jobs can run during off-peak carbon hours. | Neutral |
| CI/CD Pipelines | Time Shift + Throttling | Delay builds during peak carbon; throttle concurrent runners. | Low Cost Savings |
| Global CDN Assets | Geo-Replication | Serve from edge nodes powered by renewables; cache aggressively. | Neutral |
Configuration Template
Use this TypeScript configuration to bootstrap a carbon-aware environment.
// carbon-config.ts
export interface CarbonConfig {
provider: 'watttime' | 'electricity-maps';
apiKey: string;
thresholds: {
elastic: number; // Max intensity for batch jobs
soft: number; // Max intensity for soft real-time
};
regions: {
primary: string;
fallback: string[];
latencyBudgets: { [region: string]: number }; // ms
};
cache: {
ttlSeconds: number;
driver: 'memory' | 'redis';
};
policies: {
maxDelayMs: number;
allowGeoShift: boolean;
dataTransferLimitGB: number;
};
}
export const productionConfig: CarbonConfig = {
provider: 'watttime',
apiKey: process.env.CARBON_API_KEY!,
thresholds: {
elastic: 250,
soft: 400
},
regions: {
primary: 'us-east-1',
fallback: ['us-west-2', 'eu-central-1'],
latencyBudgets: {
'us-east-1': 20,
'us-west-2': 60,
'eu-central-1': 120
}
},
cache: {
ttlSeconds: 300,
driver: 'redis'
},
policies: {
maxDelayMs: 3600000, // 1 hour max delay for elastic
allowGeoShift: true,
dataTransferLimitGB: 10
}
};
Quick Start Guide
- Install Dependencies:
npm install @watttime/sdk cache-manager redis
- Initialize Client:
Create a
CarbonScheduler instance using the configuration template. Ensure environment variables are set.
- Wrap Critical Path:
For a batch job, wrap execution logic:
const decision = await scheduler.evaluateWorkload('us-east-1', 'elastic');
if (!decision.shouldExecute) {
console.log(`Delaying job by ${decision.delayMs}ms due to carbon intensity ${decision.intensity}`);
await sleep(decision.delayMs);
}
runJob();
- Monitor Dashboard:
Create a Grafana panel visualizing
carbon_intensity_current vs. workload_execution_count. Verify that execution drops during intensity spikes.
- Iterate Thresholds:
Review weekly reports. Adjust thresholds to meet emission targets without causing backlog accumulation.
Carbon-aware computing transforms sustainability from a reporting exercise into an active engineering discipline. By integrating grid intelligence into your orchestration layer, you reduce environmental impact, align with regulatory mandates, and often uncover cost efficiencies hidden in the grid's temporal variance. The infrastructure is available; the implementation is a matter of architectural priority.