Automatically Start EC2 and RDS on a Schedule Using AWS Step Functions
Introduction
If you’re running non-production AWS environments — development, staging, QA — there’s a good chance your EC2 instances and RDS databases are sitting idle for 16 hours a day, quietly burning through your cloud budget. Nights, weekends, holidays: the meter keeps running even when nobody’s working.
The obvious fix is to stop resources when they’re not needed and start them back up when they are. The problem is doing that reliably without babysitting it manually every morning, and without accidentally forgetting to bring something up before the team arrives.
This post walks through a small open-source project I built to solve exactly that. It uses three native AWS services — EventBridge, Step Functions, and the AWS SDK integration built into Step Functions — to automatically start EC2 instances and RDS databases on a schedule. No Lambda functions, no custom runtime code to maintain. Just a config file and a CDK stack you deploy once and forget about.
What This Project Does
This project is a serverless automation tool built with AWS CDK (Python) that automatically starts EC2 instances and RDS databases on a schedule — so you don’t pay for compute time when nobody’s using it.
The Problem It Solves
In dev/staging environments, you typically don’t need servers running overnight or on weekends. Manually starting them each morning is tedious and error-prone. This project fully automates that workflow.
Architecture
EventBridge Rule(s) → AWS Step Function → EC2 / RDS Resources
(cron schedule) (orchestrator) (started/rebooted)
Three AWS services are wired together:
- Amazon EventBridge — fires on a cron schedule (e.g. „8:00 AM Mon–Fri”)
- AWS Step Functions — receives the event and processes a list of resources one by one, with optional delays between each
- EC2 / RDS SDK calls — the Step Function calls AWS APIs directly (no Lambda needed) to start instances and clusters
What It Can Do
- Start EC2 instances on a schedule
- Reboot EC2 instances on a schedule
- Start RDS DB clusters (Aurora)
- Start RDS DB instances
- Multiple independent schedules — e.g. a dev group starts at 8 AM and a QA group starts at 9 AM
- Sequential startup with configurable delays — useful when the App Server must wait for the database to come up first
- Least-privilege IAM — the Step Function role only has permission to touch the exact resource IDs listed in your config file, not all resources in the account
Configuration
Everything is driven by a single event_rules.json file. No code changes needed to add/remove resources or change schedules:
{
"event_rules": [
{
"ruleId": "dev-environment",
"cron": { "minute": "0", "hour": "8", "week_day": "MON-FRI", "month": "*", "year": "*" },
"instances": [
{ "action": "StartDBCluster", "DBClusterIdentifier": "dev-aurora-cluster", "delay": 30 },
{ "action": "StartInstances", "instanceId": "i-0abc123", "delay": 10 },
{ "action": "StartInstances", "instanceId": "i-0def456", "delay": 0 }
]
}
]
}
The delay field tells the Step Function to wait N seconds after starting a resource before moving on to the next one — so you can, for example, wait 30 seconds for the database to boot before starting the app server.
How to Deploy
Prerequisites
- AWS CLI configured (
aws configure) - Node.js installed (required by AWS CDK CLI)
- Python 3.8+
- AWS CDK CLI:
npm install -g aws-cdk
Step 1 — Configure your resources
Edit event_rules.json and add your EC2 instance IDs and RDS identifiers with the desired cron schedules. All times are UTC.
Step 2 — Bootstrap CDK (first time only)
This creates the S3 bucket and IAM roles CDK needs to deploy assets in your account:
cdk bootstrap aws://<YOUR_ACCOUNT_ID>/<YOUR_REGION> # e.g. cdk bootstrap aws://123456789012/us-east-1
Step 3 — Preview what will be deployed
cdk synth
This prints the CloudFormation template. Inspect the IAM policy to confirm it lists only your specific resource ARNs (not wildcards).
Step 3 — Deploy
cdk deploy
CDK will show you a summary of IAM changes and ask for confirmation before deploying. The stack creates:
- One EventBridge rule per entry in
event_rules.json - One Step Function (shared by all rules)
- One IAM role scoped to the exact resources in your config
Step 5 — Verify
In the AWS Console:
- EventBridge → Rules — confirm the rule(s) exist with the correct cron
- Step Functions — confirm the state machine was created
- Wait for the scheduled time (or manually trigger the rule) and check Executions in the Step Function console
Updating the schedule or resource list
Edit event_rules.json, then run: cdk deploy
CDK detects the diff and updates only what changed. The IAM policy is automatically regenerated to match the new resource list.
You can add more event rules to start, stop, or restart more instances.
Teardown
cdk destroy
This removes all resources created by the stack from your AWS account.
Summary
This project is intentionally minimal. There’s no application server, no database, no dependencies at runtime — just a Step Function that fires on a schedule and makes a handful of AWS API calls. That simplicity is the point: fewer moving parts means less to break, less to monitor, and less to pay for.
The key design decisions worth highlighting:
- No Lambda — Step Functions can call EC2 and RDS APIs directly via the AWS SDK integration, which removes an entire layer of code and infrastructure
- Config-driven — adding a new resource or changing a schedule is a one-line edit to
event_rules.jsonfollowed bycdk deploy, no code changes needed - Least-privilege IAM — the IAM role is automatically scoped to the exact resource ARNs in your config file, so a misconfigured or compromised execution can’t touch anything outside your defined list
In practice, for a team of 5–10 engineers with a handful of dev/staging instances, this kind of setup typically saves 40–60% of EC2/RDS costs in non-production environments — just by not running things when no one’s around.
The full source is on GitHub. Clone it, drop in your instance IDs, and run cdk deploy. That’s genuinely all there is to it.