In Amazon EventBridge Scheduler, what is the difference between a rate() expression, a cron() expression, and an at() expression?
answer
- interval, calendar, or once
- six fields, not five
- one day field must be ?
- the one-shot form is at()
- time zone is a separate setting
basics
~20 sEventBridge Scheduler supports three expression forms: rate() fires on a fixed interval, cron() fires on a calendar pattern with six fields, and at() fires exactly once at a given timestamp. Time zone is set separately.
solid answer
~40 s`rate(5 minutes)` is the simplest form — a fixed interval measured from when the schedule starts, with units of minutes, hours or days. `cron(0 12 * * ? *)` is the calendar form: six fields — minutes, hours, day-of-month, month, day-of-week, year — where one of the two day fields must be `?` because you cannot constrain both. `at(2026-03-01T09:00:00)` is a one-time schedule that fires once at that timestamp and then sits there completed unless you set `ActionAfterCompletion` to `DELETE`. Whichever form you use, the timestamp is interpreted in UTC by default; setting `ScheduleExpressionTimezone` to an IANA zone such as `America/New_York` makes cron and at expressions follow local wall-clock time, including daylight-saving shifts.
code
bash · 11 linesaws scheduler create-schedule --name nightly-report \
--schedule-expression "cron(0 2 * * ? *)" \
--schedule-expression-timezone "America/New_York" \
--flexible-time-window "Mode=OFF" \
--target '{"Arn":"arn:aws:lambda:us-east-1:111122223333:function:report","RoleArn":"arn:aws:iam::111122223333:role/scheduler-invoke"}'
aws scheduler create-schedule --name expire-hold-9f2 \
--schedule-expression "at(2026-03-01T09:00:00)" \
--action-after-completion DELETE \
--flexible-time-window "Mode=OFF" \
--target '{"Arn":"arn:aws:lambda:us-east-1:111122223333:function:expire-hold","RoleArn":"arn:aws:iam::111122223333:role/scheduler-invoke"}'go deeper
Recognise all three forms on sight and know that cron has six fields with a ? in one day position. Be ready to write a simple daily or every-N-minutes expression without looking it up.
Explain that rate is relative to activation while cron is absolute on the calendar, and describe how the time-zone setting makes cron follow local wall-clock time across daylight saving.
Show that you think about lifecycle: one-time schedules accumulate unless ActionAfterCompletion deletes them, and start and end dates bound recurring schedules so cleanup is not a cron job you have to write.
Own the convention across teams — where schedule definitions live, who reviews them, and how time-zone policy is set so financial or regulatory jobs are not silently an hour off after a clock change.
## Three ways to say when Amazon EventBridge Scheduler accepts exactly three kinds of schedule expression. Choosing between them is mostly about whether you mean *every so often*, *at this point on the calendar*, or *once*. ### rate(value unit) ``` rate(1 minute) rate(5 minutes) rate(12 hours) rate(7 days) ``` The unit is minutes, hours or days, and it is singular when the value is 1 and plural otherwise — `rate(1 minutes)` is rejected. A rate schedule is *relative*: the clock starts when the schedule becomes active (or at its `StartDate`), not on a calendar boundary. `rate(1 hour)` created at 09:17 fires at 10:17, not at 10:00. That relativity is the main reason people are surprised by rate schedules, and the reason anything that must land on a boundary should be cron. ### cron(fields) ``` cron(0 12 * * ? *) # every day at 12:00 cron(0 9 ? * MON-FRI *) # weekdays at 09:00 cron(0 0 1 * ? *) # first of every month at midnight ``` There are **six** fields — minutes, hours, day-of-month, month, day-of-week, year — one more than a Unix crontab, which is the single most common syntax mistake. The other rule that trips people is the `?`: day-of-month and day-of-week overlap, so exactly one of them must be `?` (meaning "no specific value") whenever the other is constrained. `cron(0 12 * * MON *)` is invalid; `cron(0 12 ? * MON *)` is what you meant. ### at(timestamp) ``` at(2026-03-01T09:00:00) ``` A one-time schedule. The format is `yyyy-mm-ddThh:mm:ss` with no offset or `Z` suffix — the offset comes from `ScheduleExpressionTimezone`, not from the string. One-time schedules are the reason Scheduler exists as a separate service: they let you create a timer per business entity rather than a rule per pattern. A fired one-time schedule does not disappear on its own. It stays in your account in a completed state and continues to count against the schedules quota unless you either delete it yourself or create it with `ActionAfterCompletion` set to `DELETE`, which tells Scheduler to clean up after the final invocation. Forgetting this is how accounts accumulate hundreds of thousands of dead schedules. ## Time zones and daylight saving All three forms default to UTC. `ScheduleExpressionTimezone` takes an IANA time-zone name, and when you set one, cron and at expressions are evaluated against local wall-clock time in that zone. This is what makes "send the daily digest at 08:00 local" correct year-round: when the zone shifts for daylight saving, the schedule follows the wall clock rather than drifting an hour. Rate expressions are intervals and are unaffected by the zone. `StartDate` and `EndDate` bound when a recurring schedule is active, which is useful for campaigns and trials — the schedule stops firing after the end date without you writing cleanup code. ## Choosing between them - Use **rate** for background maintenance where the exact minute is irrelevant: polling, refreshing a cache, reaping stale rows. - Use **cron** when a human or a downstream system cares about the calendar: business-hours reports, month-end billing runs, weekday-only jobs. - Use **at** for per-entity deferred work: expire this reservation, retry this payment, remind this user — one schedule per thing, created when the thing is created. ## Validation happens at creation Scheduler validates the expression when you create or update a schedule, so a malformed cron fails fast rather than silently never firing. That is worth saying out loud in an interview, because the failure mode people fear — a job that quietly never runs — usually comes from a *valid* expression meaning something other than what you intended, such as a six-field cron whose year field you thought was the day-of-week.
- What happens to a one-time at() schedule after it fires?It remains in the account in a completed state and still counts toward the schedules quota. Set `ActionAfterCompletion` to `DELETE` when creating it and Scheduler removes it after the final invocation; otherwise you need your own cleanup, which is a real operational burden when you create one schedule per business entity.
- Why does an EventBridge Scheduler cron expression require a ? in a day field?Day-of-month and day-of-week describe the same axis, so constraining both is ambiguous. `?` means "no specific value" and marks which of the two you are not using. `cron(0 12 * * MON *)` is rejected; `cron(0 12 ? * MON *)` means noon every Monday.
- How do you make a daily job fire at 08:00 local time through a change in daylight saving?Use a cron expression with `ScheduleExpressionTimezone` set to the IANA zone, for example `America/New_York`. Scheduler evaluates the expression against local wall-clock time, so the job follows the clock change instead of drifting by an hour twice a year.
saying these in an interview costs you the question
- Writes a five-field Unix crontab expression
- Constrains both day-of-month and day-of-week
- Assumes rate schedules align to the top of the hour
- Thinks a fired one-time schedule deletes itself by default
- Puts a Z or offset inside the at() timestamp