AWS EventBridge Scheduler: Starting and Stopping EC2 on a Schedule
Starting and stopping EC2 instances on a cron schedule with EventBridge Scheduler calling the EC2 API directly, no Lambda involved.
EventBridge Scheduler can start and stop EC2 instances on a cron schedule without any Lambda function in between.
Building
Start an EC2 instance for the test.
aws ec2 run-instances \ --image-id ami-0f9fe1d9214628296 \ --count 1 \ --instance-type t2.micro \ --security-group-ids <SECURITY_GROUP_IDS> \ --subnet-id <SUBNET_ID>The ScheduleStartExpression and ScheduleStopExpression parameters on lines 11-16 define the schedule using cron expression.
AWSTemplateFormatVersion: '2010-09-09'Description: EC2 scheduler
Parameters: Prefix: Type: String Default: ec2-scheduler InstanceIds: Type: String Description: '"i-1234567890abcdefg", "..."' ScheduleStartExpression: Type: String Default: 'cron(0 7 * * ? *)' ScheduleStopExpression: Type: String Default: 'cron(0 22 * * ? *)' ScheduleTimezone: Type: String Description: IANA timezone identifier. For the full list, refer to https://en.wikipedia.org/wiki/List_of_tz_database_time_zones Default: UTC AllowedValues: - UTC - Japan
Resources: ScheduleStart: Type: AWS::Scheduler::Schedule Properties: Name: !Sub ${Prefix}-schedule-start ScheduleExpression: !Ref ScheduleStartExpression ScheduleExpressionTimezone: !Ref ScheduleTimezone FlexibleTimeWindow: Mode: 'OFF' State: ENABLED Target: Arn: arn:aws:scheduler:::aws-sdk:ec2:startInstances Input: !Sub |- { "InstanceIds": [${InstanceIds}] } RoleArn: !GetAtt Role.Arn
ScheduleStop: Type: AWS::Scheduler::Schedule Properties: Name: !Sub ${Prefix}-schedule-stop ScheduleExpression: !Ref ScheduleStopExpression ScheduleExpressionTimezone: !Ref ScheduleTimezone FlexibleTimeWindow: Mode: 'OFF' State: ENABLED Target: Arn: arn:aws:scheduler:::aws-sdk:ec2:stopInstances Input: !Sub |- { "InstanceIds": [${InstanceIds}] } RoleArn: !GetAtt Role.Arn
Role: Type: AWS::IAM::Role Properties: RoleName: !Sub ${Prefix}-role AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - scheduler.amazonaws.com Action: - sts:AssumeRole Policies: - PolicyName: ec2 PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - ec2:StartInstances - ec2:StopInstances Resource: !Sub arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:instance/i-*Deploy the CloudFormation stack with the following command:
aws cloudformation deploy \ --template-file ./template.yaml \ --stack-name ec2-scheduler \ --parameter-overrides InstanceIds='"<INSTANCE_ID1>", "<INSTANCE_ID2>"' \ --capabilities CAPABILITY_NAMED_IAMTesting
The provisioned EventBridge Scheduler will automatically start the EC2 instance at 7:00 AM and stop it at 10:00 PM.
Cleaning Up
Clean up all the AWS resources provisioned during this example with the following command:
aws ec2 terminate-instances --instance-ids <INSTANCE_IDS>aws cloudformation delete-stack --stack-name ec2-schedulerConclusion
Setting up two EventBridge Schedules with cron expressions got an EC2 instance starting at 7 AM and stopping at 10 PM automatically. Targeting the EC2 API directly through arn:aws:scheduler:::aws-sdk:ec2:startInstances is what keeps this setup free of any Lambda function — EventBridge Scheduler can call the AWS SDK directly, so the schedule and the IAM role granting ec2:StartInstances/ec2:StopInstances are the entire solution. The one setting worth double-checking before relying on this for cost savings is ScheduleExpressionTimezone: since it defaults to UTC in the template, a schedule meant to align with business hours in another timezone needs that parameter set explicitly, or instances will start and stop several hours off from what’s expected.
Related posts
Sign in with Slack Using Cognito User Pools and OIDC
Federating Cognito user pools with Slack over OIDC and wiring "Sign in with Slack" into a Next.js app with Amplify.
Deploying FastAPI on AWS Lambda with Lambda Web Adapter
This example guides you through the process of developing API backends with FastAPI using Lambda Web Adapter.
API Gateway WebSocket: Implementing a Mock Integration
Building an API Gateway WebSocket API entirely with mock integrations, returning canned responses with no backend Lambda involved.
Uploading to S3 Through CloudFront Pre-Signed URLs
CloudFront signed URLs let you upload to S3 through a custom domain—useful when direct S3 pre-signed URLs are not an option.
From Monolith to Microservices in AWS: Three Migration Patterns
Inspired by Monolith to Microservices, this example explains monolith-to-microservices patterns on AWS.
