AWS bills have a way of growing faster than the infrastructure that generates them. On a recent engagement with a fintech client running workloads across multiple AWS accounts, we identified that a significant portion of their EC2 spend was on instances running during off-hours — dev/staging environments, batch processing nodes, and internal tools that nobody used between 8 PM and 8 AM.
The solution was straightforward: automated EC2 hibernation and stop/start scheduling using Terraform and Lambda. The result was a 10-15% reduction in their monthly AWS bill.
The Problem: Always-On Is Expensive
The client had roughly 200 EC2 instances across 4 AWS accounts. About 40% of these were non-production workloads — development environments, QA clusters, staging replicas, and internal dashboards. These instances ran 24/7 despite being actively used only during business hours (roughly 10 hours/day on weekdays).
Quick math: running an instance 24/7 costs 168 hours/week. If it is only needed for 50 hours/week, you are paying for 118 hours of idle time — 70% waste.
The Solution: Tag-Based Scheduling with Lambda
Rather than building a complex scheduling system, we implemented a lightweight, tag-based approach:
Step 1: Define scheduling tags
We added two tags to EC2 instances:
Schedule: business-hours— stop at 8 PM IST, start at 8 AM IST, weekdays onlySchedule: extended— stop at 11 PM, start at 7 AM, all days- No tag = always on (production workloads)
Step 2: Lambda functions for start/stop
Two Lambda functions handled the automation:
- StopInstances — queries EC2 for running instances with a matching
Scheduletag, then stops or hibernates them - StartInstances — queries for stopped/hibernated instances with matching tags and starts them
We chose hibernation over stop/start where possible because hibernation preserves in-memory state, making resume faster and avoiding cold-start issues for development environments.
Step 3: EventBridge rules for scheduling
CloudWatch EventBridge (cron) rules triggered the Lambda functions at the defined times. Each schedule profile had its own pair of rules.
Step 4: Terraform for everything
The entire setup — Lambda functions, IAM roles, EventBridge rules, and tag policies — was defined in Terraform. This meant:
- The scheduling infrastructure itself was version-controlled and reproducible
- Adding a new schedule profile required only a new Terraform variable, not code changes
- The same module was deployed across all 4 AWS accounts
Multi-Account Strategy
The client used AWS Organizations with separate accounts for dev, staging, production, and shared services. We deployed the scheduling Lambda in the shared services account and used cross-account IAM roles to manage instances in the other accounts.
This centralized approach meant:
- One place to manage all scheduling logic
- Consistent tagging policies enforced via AWS Config rules
- A single CloudWatch dashboard showing cost impact across all accounts
Handling Edge Cases
The simple approach works well, but production taught us a few lessons:
- Override mechanism — engineers sometimes need instances during off-hours. We added an
Override: keep-runningtag that the Lambda functions respect, with a TTL that auto-removes the override after 24 hours - RDS instances — we extended the same pattern to RDS instances in non-production accounts, stopping dev databases during off-hours
- EBS volumes — stopped instances still incur EBS costs. For truly ephemeral dev environments, we switched to instance store volumes where possible
- Elastic IPs — stopped instances with allocated EIPs incur charges. We automated EIP release/allocation as part of the stop/start cycle
Results
After one month of running the scheduling automation:
- 10-15% reduction in total EC2 spend across all accounts
- Zero impact on developer productivity — instances were available during all working hours
- Faster dev environments — hibernation preserved state, so developers resumed exactly where they left off
- Better resource awareness — the tagging exercise itself revealed orphaned instances and unused resources
Key Takeaways
- Start with tagging — you cannot optimize what you cannot categorize
- Lambda + EventBridge is the simplest scheduling architecture on AWS
- Hibernation is preferable to stop/start for developer experience
- Deploy scheduling infrastructure with Terraform, not ClickOps
- Multi-account setups benefit from centralized scheduling with cross-account roles
- Always provide an override mechanism for engineers who need off-hours access
Cost optimization does not require complex tools or third-party platforms. A well-designed tagging strategy and a few Lambda functions can deliver significant savings with minimal operational overhead.