DIY EventBridge Scheduler and Lambda Start/Stop vs ZopNight
A cron rule and a small function can stop your dev instances tonight. This page lists what that build has to handle over time, using AWS's own documentation.
The basic build
The usual pattern has two parts. An EventBridge Scheduler schedule fires at 19:00 and 08:00. A Lambda function receives the event, finds the instances (often by tag) and calls StopInstances or StartInstances. For a single account with a handful of EC2 instances, that is a small build. On cost, EventBridge Scheduler includes 14 million free invocations a month (EventBridge pricing).
You may not even need the Lambda. Scheduler’s universal targets can call AWS API operations directly. Only read-only actions are blocked: those starting with prefixes such as describe, list and get (universal targets). The catch is that without a describe call, a schedule cannot look up instances by tag, so you list instance IDs in each schedule, or you keep the function.
What you end up maintaining
IAM, twice. Scheduler needs an execution role, “an IAM role that EventBridge Scheduler assumes in order to interact with other AWS services on your behalf” (setting up). The Lambda needs its own role with EC2 and RDS permissions. Across accounts, each target account needs a role the function can assume with sts:AssumeRole, and a trust policy that allows it (cross-account roles).
Retries and failure handling. Scheduler offers “at-least-once event delivery” (what is Scheduler). A retry policy allows up to 185 attempts within a window of up to 86,400 seconds (RetryPolicy). Exhausted events can go to an SQS dead-letter queue (DLQ). But retries cover delivery to your target, not what your code does. If the function gets a throttle or a capacity error on one instance out of forty, retrying and alerting on that is code you write. At-least-once delivery also means the function should be safe to run twice.
Time zones and daylight saving. Scheduler evaluates cron in UTC or an IANA time zone you set per schedule. It adjusts for daylight saving with two edge cases. When clocks spring forward, a time that does not exist that day “is skipped”. When they fall back, the schedule “runs only once” (schedule types). Check that no start or stop falls inside your zone’s changeover hour, and remember that teams in different time zones need separate schedules.
Timeouts. A standard Lambda invocation can run for at most 900 seconds (Lambda timeout). One function looping over a large fleet in several Regions and accounts has to fit inside that, or fan out.
State the cloud does not keep for you. RDS restarts a stopped instance by itself: “If you don’t manually start your DB instance after it is stopped for seven consecutive days, RDS automatically starts your DB instance for you” (RDS docs). A database you meant to keep off over a long holiday needs your code to notice and stop it again. And EC2 will not stop everything: “You can’t stop or hibernate instance store-backed instances” (StopInstances).
Exceptions and history. Someone will ask to keep staging up tonight for a demo. In a tag-driven build, that means editing a tag and remembering to put it back. And when a VM is unexpectedly off in the morning, the answer is in CloudWatch Logs, if the function logged it.
When DIY is enough
Build it yourself if you have one or two accounts, EC2 and RDS only, one time zone, and an engineer who will own the code. It stays cheap, and nothing leaves your account.
When ZopNight is the better trade
- Retries and a failure trail built in. If a start or stop fails, ZopNight retries up to 3 times, then moves it to a dead-letter queue and marks it failed where the UI can show it. Failures can alert Slack, Teams, Google Chat or a webhook.
- Exceptions that clean up after themselves. An override keeps a resource on or forces it off until a set time, with a reason, then ends. One active override per resource prevents conflicts.
- History by trigger. Every state change is recorded with what caused it: which schedule, which user, or which override.
- Accounts and clouds at scale. Each AWS account connects through a keyless assumed role, and the same schedules cover GCP and Azure.
- More than EC2 and RDS. Schedules also cover EKS clusters and node groups, ECS services, Auto Scaling groups, Lambda concurrency, SageMaker notebooks and single Kubernetes namespaces. See EC2 scheduling and EKS scheduling.
| ZopNight | EventBridge Scheduler and Lambda | |
|---|---|---|
| Who writes the logic | Built in | You: targeting, error handling, logging |
| Retries | Up to 3 retries per action, then a dead-letter queue | Delivery retries up to 185; action-level retries are your code |
| Time zones | IANA time zone per schedule | IANA time zone per schedule; DST gaps skipped |
| Temporary exceptions | Overrides with reason and expiry | Edit tags or schedules by hand |
| Running cost | See zop.dev/pricing | Lambda and Scheduler usage; 14 million invocations a month free |
Questions we get a lot.
If yours isn't here, email us and we'll answer directly.
Do I need Lambda to stop EC2 instances with EventBridge Scheduler?
Not always. Scheduler's universal targets can call non-read-only AWS API actions directly, so a schedule can call StopInstances with fixed instance IDs. Looking instances up by tag needs a describe call, which universal targets do not allow, so tag-based targeting usually needs a function.
How does EventBridge Scheduler handle daylight saving time?
It adjusts automatically for the time zone you set. AWS documents that an invocation falling in the skipped hour when clocks spring forward is skipped, and one in the repeated hour when clocks fall back runs only once.
Why did my stopped RDS instance start on its own?
RDS automatically starts a DB instance that has been stopped for seven consecutive days. A scheduler has to stop it again if you want it to stay off longer.