AWS EventBridge Cron Generator
Generate 6-field AWS EventBridge cron expressions, see the next UTC run times, and copy ready-to-paste CloudFormation, Terraform and CLI snippets.
at 12:00 UTC
Next 5 runs (UTC)
Calculating…
Common schedules
Syntax
*— any value5— exact value1,3,5— list of values1-5— inclusive range*/15— every 15 units (also0/15in AWS)?— required for one of day-of-month or day-of-week2#1— first Monday of the month (Nth weekday)6L— last Friday of the monthLin day-of-month — last day of the month
AWS EventBridge cron uses a 6-field expression wrapped in cron(...) — cron(0 9 ? * MON-FRI *) runs every weekday at 9 AM UTC. The fields are minute, hour, day-of-month, month, day-of-week, year, with a mandatory ? for one of dom or dow. Use the form above to build an expression with a live UTC run preview, and copy-paste IaC snippets for CloudFormation, Terraform and the AWS CLI.
Why does AWS EventBridge cron need 6 fields?
cron(minute hour day-of-month month day-of-week year)
0 9 ? * MON-FRI *
0–59 0–23 1–31 / ? 1–12 1–7 / ? *
Five gotchas, in priority order:
- Six fields, not five. Standard Unix cron is
* * * * *. AWS is* * * * ? *— the trailing*for year is mandatory, even though it’s almost always*. ?is required. Day-of-month and day-of-week can’t both be*(or both restricted) — exactly one of them must be?. The?means “I’m not specifying this field”.- Day-of-week is 1–7, not 0–6. AWS numbers Sunday as
1through Saturday as7, where standard Unix cron numbers Sunday as0through Saturday as6. Use the named form (SUN,MON, …,SAT) to sidestep the off-by-one entirely. - All times are UTC. Original EventBridge Rules have no timezone option. Use EventBridge Scheduler (the newer service) if you need a timezone — same cron syntax, but you set
ScheduleExpressionTimezoneseparately. - No
@daily,@hourlyshortcuts. Those are Vixie cron extensions. AWS only accepts the explicit 6-field form.
Common AWS cron expressions
| Schedule | Expression |
|---|---|
| Every 5 minutes | cron(*/5 * * * ? *) |
| Every 15 minutes | cron(0/15 * * * ? *) |
| Every hour, on the hour | cron(0 * * * ? *) |
| Daily at 2 AM UTC | cron(0 2 * * ? *) |
| Every weekday at 9 AM UTC | cron(0 9 ? * MON-FRI *) |
| First Monday of each month, 9 AM UTC | cron(0 9 ? * 2#1 *) |
| Last Friday of each month, 10:15 PM UTC | cron(15 22 ? * 6L *) |
| Twice daily at 6 AM and 6 PM UTC | cron(0 6,18 * * ? *) |
How do I run on the first Sunday or last Friday of the month?
AWS EventBridge accepts two day-of-week operators that standard Unix cron doesn’t — # for nth occurrence, and L for last. They’re the only way to express schedules like “first Sunday of the month” or “last business day” without external orchestration.
#— the nth occurrence.SUN#1is the first Sunday of the month,MON#3is the third Monday,FRI#5is the fifth Friday (skipped in months with only four). The numeric form works too —1#1equalsSUN#1,2#1equalsMON#1. Valid only in the day-of-week field;nmust be between 1 and 5.L— the last. In day-of-week,FRIL(or6L) is the last Friday of the month. In day-of-month, plainLis the last day of the month, andL-3is three days before the last day.
| Schedule | Expression |
|---|---|
| First Sunday of the month, 9 AM UTC | cron(0 9 ? * SUN#1 *) |
| First Monday of the month, 9 AM UTC | cron(0 9 ? * MON#1 *) (or cron(0 9 ? * 2#1 *)) |
| Third Wednesday of the month, noon UTC | cron(0 12 ? * WED#3 *) |
| Last Friday of the month, 10 PM UTC | cron(0 22 ? * FRIL *) (or cron(0 22 ? * 6L *)) |
| Last day of every month, 11 PM UTC | cron(0 23 L * ? *) |
| Three days before month end, midnight UTC | cron(0 0 L-3 * ? *) |
Two practical gotchas:
Lstandalone in day-of-week is Saturday, not “any last weekday”. AWS treats Saturday as the last day of the week (because day-of-week starts at Sunday=1). To target a specific last weekday of the month, prefix it:5LorFRILfor last Friday.- You still need
?in the field you’re not constraining.cron(0 9 ? * SUN#1 *)keeps?for day-of-month — without it AWS rejects the expression because dom and dow can’t both be specified.
Both operators behave identically in EventBridge Rules and EventBridge Scheduler — the timezone capability is the only difference between the two services, not the cron syntax.
How do I convert my local time to UTC for AWS cron?
If your team is in IST (UTC+5:30) and you want a job to run at 9 AM IST:
9:00 IST = 03:30 UTC → cron(30 3 * * ? *)
If you’re in PST (UTC-8) and want 9 AM PST:
9:00 PST = 17:00 UTC → cron(0 17 * * ? *)
Daylight saving complicates things — UTC has none, so a “9 AM PST” job will silently run an hour earlier in the eyes of users on PDT. If that matters, use EventBridge Scheduler with a timezone instead of converting to UTC.
How do I use the expression in CloudFormation, Terraform or the CLI?
AWS CLI — put-rule with a schedule expression:
aws events put-rule \
--name daily-cleanup \
--schedule-expression "cron(0 2 * * ? *)" \
--state ENABLED
CloudFormation — EventBridge Rules:
DailyCleanupRule:
Type: AWS::Events::Rule
Properties:
Name: daily-cleanup
ScheduleExpression: 'cron(0 2 * * ? *)'
State: ENABLED
Targets:
- Id: cleanup-lambda
Arn: !GetAtt CleanupFunction.Arn
CloudFormation — EventBridge Scheduler (with timezone):
DailyCleanupSchedule:
Type: AWS::Scheduler::Schedule
Properties:
Name: daily-cleanup
ScheduleExpression: 'cron(0 9 * * ? *)'
ScheduleExpressionTimezone: 'America/Los_Angeles'
FlexibleTimeWindow:
Mode: 'OFF'
Target:
Arn: !GetAtt CleanupFunction.Arn
RoleArn: !GetAtt SchedulerRole.Arn
Terraform — EventBridge Rule:
resource "aws_cloudwatch_event_rule" "daily_cleanup" {
name = "daily-cleanup"
schedule_expression = "cron(0 2 * * ? *)"
}
Terraform — EventBridge Scheduler:
resource "aws_scheduler_schedule" "daily_cleanup" {
name = "daily-cleanup"
schedule_expression = "cron(0 9 * * ? *)"
schedule_expression_timezone = "America/Los_Angeles"
flexible_time_window {
mode = "OFF"
}
target {
arn = aws_lambda_function.cleanup.arn
role_arn = aws_iam_role.scheduler.arn
}
}
Triggering a Lambda from EventBridge cron — the permission gotcha
EventBridge can target a Lambda directly, but the call only goes through if Lambda also grants invocation permission to the EventBridge service principal. Miss this and the schedule fires successfully while Lambda silently rejects every invocation with AccessDeniedException in CloudWatch.
Rules invoke Lambda via a resource-based permission. Add this alongside the AWS::Events::Rule (or aws_cloudwatch_event_rule):
RuleInvokeLambdaPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !Ref CleanupFunction
Principal: events.amazonaws.com
SourceArn: !GetAtt DailyCleanupRule.Arn
resource "aws_lambda_permission" "allow_events" {
statement_id = "AllowExecutionFromEventBridge"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.cleanup.function_name
principal = "events.amazonaws.com"
source_arn = aws_cloudwatch_event_rule.daily_cleanup.arn
}
Scheduler is different — it invokes through an IAM role. The Scheduler examples above already include RoleArn/role_arn; that role needs an inline or attached policy granting lambda:InvokeFunction on the target Lambda’s ARN. No AWS::Lambda::Permission resource is required for Scheduler-invoked targets.
EventBridge Scheduler vs EventBridge Rules
For new schedule-driven projects, use EventBridge Scheduler. Launched in late 2022, it’s AWS’s purpose-built cron service and has several capabilities Rules don’t.
Per-schedule timezones
EventBridge Rules evaluate every cron expression in UTC. Scheduler accepts a ScheduleExpressionTimezone (any IANA zone — America/New_York, Asia/Tokyo) per schedule, so cron(0 9 * * ? *) with timezone America/Los_Angeles fires at 9 AM Pacific year-round, daylight saving included. The cron string itself is unchanged.
One-time schedules with at(...)
Scheduler accepts a third expression type Rules don’t — at(2026-12-25T09:00:00) runs the target exactly once at that wall-clock time. Pair with a timezone for any zone. Use it for delayed work (invoice generation 30 days after signup, account-deletion grace periods, scheduled feature flags) without spinning up Step Functions.
Flexible time windows
The FlexibleTimeWindow property lets Scheduler fire any time within a window after the nominal cron time, smoothing thundering-herd patterns at scale. Mode: OFF keeps strict cron semantics; Mode: FLEXIBLE with MaximumWindowInMinutes: 15 lets the invocation drift up to 15 minutes for load-spreading.
Retries and dead-letter queues
Scheduler retries failed invocations (configurable retry count and maximum event age) and routes undeliverable events to an SQS DLQ. Rules don’t natively retry cron-triggered Lambda invocations beyond Lambda’s own async retry semantics.
Scale
A single account can hold up to a million Scheduler schedules per region. Rules have a default quota of 300 rules per event bus (raisable to a few thousand) and per-target invocation TPS capped lower than Scheduler.
When should I migrate from Rules to Scheduler?
Migrate if you need any of: timezones, one-time schedules, retry/DLQ, or more than ~300 schedules per account. The cron syntax is identical between the two services — the migration is mostly a resource-type swap (AWS::Events::Rule → AWS::Scheduler::Schedule), not a schedule re-write. Stay on Rules if your existing IaC works and you’re under those limits — there’s no deprecation pressure on Rules for cron use cases.
When should I use a cron vs a rate expression?
EventBridge accepts two schedule expression types:
rate(N units)— fires every N minutes / hours / days starting from when the rule was created. Use for “every 30 minutes” jobs where the actual minute doesn’t matter.cron(...)— fires on a specific clock pattern. Use for “every weekday at 9 AM UTC”, “the 1st and 15th of every month”, anything with calendar semantics.
If your only requirement is “run every N minutes”, rate(N minutes) is shorter and harder to get wrong. Use cron(...) when the wall-clock time matters.
Related
- Generic cron expression builder — for Unix / Linux / GitHub Actions schedules.
- Kubernetes CronJob schedules — 5-field syntax + ConcurrencyPolicy gotchas.
Frequently asked questions
- Why does AWS EventBridge cron require 6 fields instead of 5?
- AWS adds a year field after day-of-week. The full layout is `cron(minute hour day-of-month month day-of-week year)`. You can almost always leave year as `*`. The other practical difference: AWS uses 1–7 for day-of-week (Sun=1) where standard Unix cron uses 0–6 (Sun=0).
- What''s the `?` for in AWS cron expressions?
- AWS doesn't allow `*` for both day-of-month and day-of-week at the same time — you must use `?` for whichever one you're NOT constraining. So "every weekday at 9 AM" is `cron(0 9 ? * MON-FRI *)`, not `cron(0 9 * * MON-FRI *)`. The `?` literally means "no specific value" and resolves the ambiguity that Unix cron has between dom and dow.
- How do I run on the first Sunday or last Friday of the month in EventBridge?
- AWS supports two operators that standard Unix cron doesn't — `#` for the nth occurrence of a weekday, and `L` for the last. `cron(0 9 ? * SUN#1 *)` (or `1#1` numerically) runs the first Sunday at 9 AM UTC; `cron(0 22 ? * FRIL *)` (or `6L`) runs the last Friday at 10 PM UTC; `cron(0 23 L * ? *)` runs at 11 PM on the last day of every month, and `L-3` means three days before. Both operators work identically in EventBridge Rules and EventBridge Scheduler.
- Are AWS''s `#` and `L` operators the same as Quartz?
- Yes — AWS adopted them from Quartz Scheduler, the Java cron library, and the semantics match: `2#1` is the first Monday in both, `5L` is the last Thursday in both, and `L` alone in day-of-month is the last day of the month in both. The structural difference is field count — Quartz uses 7 fields (it has a leading seconds field) where AWS uses 6. To port a Quartz schedule, drop the leading seconds field; if the Quartz string has no trailing year, append ` *` for AWS's mandatory year field.
- Can I run an EventBridge schedule in a timezone other than UTC?
- **EventBridge Rules** (the original service) — no, always UTC. **EventBridge Scheduler** (launched 2022, the recommended service for new schedules) — yes, set `ScheduleExpressionTimezone` on the schedule. The cron expression syntax is identical; the timezone is configured separately as a property of the schedule, not inside the cron string.
- My expression validates in crontab.guru but fails in AWS — why?
- crontab.guru tests Unix cron (5 fields). AWS EventBridge needs 6. Most commonly: you're missing the trailing year field (add ` *`), or you're using `*` for both dom and dow (replace one with `?`). Also: AWS day-of-week is 1–7 (Sun=1), so `0` for Sunday in Unix becomes `1` in AWS.
- What is the minimum cron schedule frequency in EventBridge?
- One minute. Sub-minute schedules aren't supported by cron expressions in EventBridge — for that, use a `rate(N units)` expression (`rate(1 minute)`, `rate(5 minutes)`) or trigger from a different source like SQS or Step Functions. For sub-second event-driven dispatch use direct EventBridge rules on AWS service events.