Scheduling AWS Lambda with EventBridge: Cron & Rate Expressions Explained

A common operational need — running a Lambda function at a fixed time every day, like 8 AM for a daily report — requires a reliable scheduler. Amazon EventBridge (formerly CloudWatch Events) provides exactly this via managed, serverless scheduling with both cron and rate expressions.

TL;DR

RequirementExpression TypeExample
Every day at 8 AM UTCCroncron(0 8 * * ? *)
Every 5 minutesRaterate(5 minutes)
Every 1 hourRaterate(1 hour)
Weekdays at 9 AM UTCCroncron(0 9 ? * MON-FRI *)
First day of month at midnightCroncron(0 0 1 * ? *)

Architecture Overview

The flow is straightforward: EventBridge evaluates the schedule expression, fires an event at the defined interval, and invokes your Lambda function as the target. No polling, no infrastructure to manage.

graph LR EB["EventBridge Rule
cron(0 8 * * ? *)"] Bus["Default Event Bus"] Lambda["AWS Lambda
MyDailyReportFunction"] CW["CloudWatch Logs
/aws/lambda/..."] DLQ["SQS Dead Letter Queue"] EB -->|"Fires at 8 AM UTC"| Bus Bus -->|"Async Invoke"| Lambda Lambda -->|"Execution Logs"| CW Lambda -->|"On Failure (retries exhausted)"| DLQ
  1. EventBridge Scheduler: Holds the cron or rate rule and evaluates it against UTC time.
  2. Event Bus (default): The rule fires an event onto the default event bus at the scheduled time.
  3. Lambda Target: EventBridge invokes the Lambda function asynchronously, passing a JSON event payload.
  4. Execution Role: EventBridge requires a resource-based policy on the Lambda function granting lambda:InvokeFunction permission to the EventBridge service principal.
Analogy: Think of EventBridge as a corporate calendar system. You set a recurring meeting (the rule). At the scheduled time, the calendar automatically sends an invite (the event) to the attendee (Lambda). Lambda doesn't sit waiting — it only activates when the invite arrives.

Cron Expression Syntax (EventBridge-Specific)

EventBridge cron expressions use 6 fields, unlike the standard 5-field Unix cron. The fields are positional and strictly ordered.

PositionFieldAllowed ValuesWildcards
1Minutes0–59, - * /
2Hours0–23, - * /
3Day-of-month1–31, - * ? / L W
4Month1–12 or JAN–DEC, - * /
5Day-of-week1–7 or SUN–SAT, - * ? L #
6Year1970–2199, - * /

Critical Rule: You cannot specify a value for both Day-of-month and Day-of-week simultaneously. One of them must be ? (no specific value). This is an EventBridge-specific constraint not present in standard Unix cron.

Common Cron Examples

# Every day at 8:00 AM UTC
cron(0 8 * * ? *)

# Every Monday at 9:00 AM UTC
cron(0 9 ? * MON *)

# Every weekday (Mon-Fri) at 6:00 PM UTC
cron(0 18 ? * MON-FRI *)

# Every 15 minutes
cron(0/15 * * * ? *)

# First day of every month at midnight UTC
cron(0 0 1 * ? *)

Rate Expression Syntax

Rate expressions are simpler and ideal for fixed intervals. The syntax is rate(value unit).

UnitSingularPlural
Minutesrate(1 minute)rate(5 minutes)
Hoursrate(1 hour)rate(2 hours)
Daysrate(1 day)rate(7 days)

Both singular and plural unit forms are accepted by AWS. rate(1 hour) and rate(1 hours) are both valid per AWS documentation. Rate expressions always run relative to when the rule was created or last updated — they do not align to clock boundaries.

Implementation: Step-by-Step

Step 1 — Grant EventBridge Permission to Invoke Lambda

EventBridge invokes Lambda using a resource-based policy. You must add a permission statement to your Lambda function allowing the EventBridge service principal to call it.

aws lambda add-permission \
  --function-name MyDailyReportFunction \
  --statement-id EventBridgeDailyTrigger \
  --action lambda:InvokeFunction \
  --principal events.amazonaws.com \
  --source-arn arn:aws:events:us-east-1:123456789012:rule/DailyReportRule

Step 2 — Create the EventBridge Rule

aws events put-rule \
  --name DailyReportRule \
  --schedule-expression "cron(0 8 * * ? *)" \
  --state ENABLED \
  --description "Triggers Lambda every day at 8 AM UTC"

Step 3 — Add Lambda as the Target

🔽 [Click to expand] — Full put-targets command with input transformer
aws events put-targets \
  --rule DailyReportRule \
  --targets "[
    {
      \"Id\": \"DailyReportLambdaTarget\",
      \"Arn\": \"arn:aws:lambda:us-east-1:123456789012:function:MyDailyReportFunction\",
      \"Input\": \"{\\\"source\\\": \\\"eventbridge-scheduler\\\", \\\"task\\\": \\\"daily-report\\\"}\"
    }
  ]"

Step 4 — Verify the Rule

aws events describe-rule --name DailyReportRule

CloudFormation / IaC Definition

🔽 [Click to expand] — CloudFormation YAML template
AWSTemplateFormatVersion: '2010-09-09'
Description: EventBridge rule to trigger Lambda daily at 8 AM UTC

Resources:

  DailyReportRule:
    Type: AWS::Events::Rule
    Properties:
      Name: DailyReportRule
      Description: Triggers Lambda every day at 8 AM UTC
      ScheduleExpression: "cron(0 8 * * ? *)"
      State: ENABLED
      Targets:
        - Id: DailyReportLambdaTarget
          Arn: !GetAtt MyDailyReportFunction.Arn

  LambdaInvokePermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !Ref MyDailyReportFunction
      Action: lambda:InvokeFunction
      Principal: events.amazonaws.com
      SourceArn: !GetAtt DailyReportRule.Arn

  MyDailyReportFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: MyDailyReportFunction
      Runtime: python3.12
      Handler: index.handler
      Role: arn:aws:iam::123456789012:role/LambdaExecutionRole
      Code:
        ZipFile: |
          def handler(event, context):
              print("Daily report triggered:", event)
              return {"status": "ok"}

Execution Flow Deep-Dive

sequenceDiagram participant EB as "EventBridge" participant LQ as "Lambda Async Queue" participant LF as "Lambda Function" participant CW as "CloudWatch Logs" participant DLQ as "SQS DLQ" EB->>LQ: "Async Invoke (cron fires at 8 AM UTC)" LQ-->>EB: "HTTP 200 Accepted" LQ->>LF: "Dequeue & Execute" alt "Success" LF->>CW: "Write execution logs" else "Failure (up to 2 retries)" LQ->>LF: "Retry attempt" LF->>DLQ: "Send to DLQ after retries exhausted" end
  1. Schedule Evaluation: EventBridge evaluates the cron expression against UTC. All EventBridge schedules run in UTC — there is no native timezone offset in EventBridge Rules (note: EventBridge Scheduler, a separate newer service, does support timezones).
  2. Asynchronous Invocation: EventBridge invokes Lambda asynchronously. Lambda queues the event internally and returns a 200 immediately to EventBridge.
  3. Retry Behavior: On Lambda-side failures, Lambda's asynchronous invocation retry policy applies (up to 2 retries by default). EventBridge itself does not retry failed Lambda invocations.
  4. Dead Letter Queue: Configure a Lambda DLQ (SQS or SNS) to capture events that exhaust retries.
  5. CloudWatch Logs: Lambda execution logs are written to CloudWatch Logs under /aws/lambda/MyDailyReportFunction.

EventBridge Rules vs. EventBridge Scheduler

AWS offers two scheduling mechanisms. Understanding the distinction prevents architectural mismatches.

FeatureEventBridge Rules (Scheduled)EventBridge Scheduler
Timezone supportUTC onlyNamed timezones supported
One-time schedulesNot supportedSupported
Flexible time windowsNot supportedSupported
ScaleTied to event bus limitsDesigned for high-volume schedules
Use caseSimple recurring triggersComplex, high-scale, or timezone-aware scheduling

Common Pitfalls

PitfallRoot CauseFix
Lambda never triggersMissing resource-based policy on LambdaAdd lambda:InvokeFunction permission for events.amazonaws.com
Wrong execution timeConfusing local time with UTCEventBridge Rules always use UTC; convert your target time to UTC explicitly
Cron expression rejectedBoth Day-of-month and Day-of-week are setSet one field to ? — e.g., cron(0 8 * * ? *)
Silent failuresNo DLQ configured on Lambda async invocationConfigure an SQS DLQ on the Lambda function's event invoke config
Timezone mismatchEventBridge Rules have no timezone parameterUse EventBridge Scheduler if you need named timezone support

IAM Least Privilege Reference

The Lambda execution role does not need EventBridge permissions. The trust relationship flows in one direction: EventBridge → Lambda. The only required permission is the Lambda resource-based policy shown in Step 1. If you are creating rules programmatically, the IAM identity needs events:PutRule, events:PutTargets, and lambda:AddPermission.

Wrap-Up & Next Steps

For a daily 8 AM trigger, cron(0 8 * * ? *) on an EventBridge Rule is the correct, minimal solution. If you need timezone-aware scheduling, one-time executions, or flexible time windows, migrate to EventBridge Scheduler. Always validate your cron expressions using the AWS Console's built-in schedule preview before deploying to production.

Glossary

TermDefinition
EventBridge RuleAn AWS resource that matches incoming events or fires on a schedule, routing them to one or more targets.
Cron ExpressionA 6-field time pattern (EventBridge-specific) defining when a scheduled rule fires, always evaluated in UTC.
Rate ExpressionA simple interval-based schedule syntax (rate(value unit)) that fires at a fixed frequency.
Resource-Based PolicyA Lambda permission statement that grants an external service (like EventBridge) the right to invoke the function.
Asynchronous InvocationA Lambda invocation mode where the caller (EventBridge) does not wait for the function to complete; Lambda queues and processes the event independently.

Comments

Popular posts from this blog

EC2 No Internet Access in Custom VPC: Attaching an Internet Gateway and Fixing Route Tables

IAM User vs. IAM Role: Why Your EC2 Instance Should Never Use a User

EC2 SSH Connection Timeout: The Exact Security Group Rules You Need to Fix It