AWS gives you three solid ways to schedule recurring work. What it doesn't give you — by default — is any alert when that work quietly stops happening. This article covers the three main AWS scheduling patterns, why native monitoring falls short, and the simplest approach to know when a scheduled job fails or disappears.
The most common serverless scheduling pattern. You create an EventBridge rule with a cron or rate expression, point it at a Lambda function, and AWS invokes the function on schedule.
# EventBridge cron expression — runs every day at 03:00 UTC
cron(0 3 * * ? *)
EventBridge is reliable at triggering. The silent failure happens inside the Lambda: an uncaught exception, a timeout, a downstream dependency that's unavailable. The invocation record appears in CloudWatch Logs as a success because Lambda received and ran the function — even if the business logic failed internally.
For containerized workloads, you can use EventBridge to trigger an ECS task on a schedule instead of a Lambda. The container runs, does its work, and exits. ECS scheduled tasks are useful when your job needs more memory, a longer timeout, or a richer runtime environment than Lambda allows.
The monitoring challenge is the same: ECS reports the task as complete once the container exits with code 0. If your application catches its own exceptions and exits cleanly despite an internal failure, the orchestration layer sees a success.
For jobs that need to run on an EC2 instance, you either use traditional crontab directly or AWS Systems Manager (SSM) Run Command / Maintenance Windows to dispatch scheduled shell commands. SSM gives you centralized execution history, but the fundamental observability problem is identical: a job that runs and exits 0 is recorded as successful regardless of what it actually did.
AWS provides several mechanisms to observe Lambda and EventBridge behavior, but assembling them into reliable job-failure alerts takes real effort.
You can create CloudWatch Alarms on the Errors metric for a Lambda function, which fires when the invocation throws an unhandled exception. This catches hard crashes. It does not catch: silent logic failures, jobs that time out gracefully, jobs that stop being invoked entirely (e.g. the EventBridge rule gets disabled), or Lambda functions that return success while doing nothing useful.
Lambda Destinations let you route asynchronous invocation results to an SQS queue, SNS topic, EventBridge event bus, or another Lambda — separately for success and failure. This is useful for building pipelines but still requires you to wire up a downstream failure handler and does nothing for missed runs.
You can create a metric filter that scans your Lambda logs for the string ERROR or any pattern you choose, increment a counter, and alarm on that counter. This works if your code writes structured errors to stdout. It's fragile if your error messages vary, and it still doesn't catch a Lambda that simply stops being invoked.
The most reliable approach to AWS cron job monitoring doesn't involve CloudWatch Alarms at all. It inverts the problem: instead of watching for errors, you watch for the absence of success.
At the end of each successful job run, send a ping to a monitoring service. If the service doesn't receive the ping within the expected window, it fires an alert.
For a Lambda function, it's one line:
import urllib.request
def handler(event, context):
# ... your job logic ...
do_the_work()
# Ping only fires if do_the_work() completes without exception
urllib.request.urlopen(
"https://cron.watch/ping/your-job-id",
timeout=5
)
Or with curl from a shell-based ECS task or EC2 cron:
# ECS task entrypoint
python /app/jobs/nightly_sync.py && \
curl -s https://cron.watch/ping/your-job-id
The && means the ping only fires on a clean exit. A failure, timeout, or crash means the ping never arrives. The monitoring service alerts you.
This single pattern covers all three failure modes:
You don't need separate alarms for each failure mode. One missing ping catches all of them.
A practical setup for a Lambda + EventBridge job:
For ECS Scheduled Tasks, the same approach applies: the ping goes at the end of the container's main process. For EC2 cron via SSM, it goes at the end of the shell command.
CronWatch uses exactly this pattern. Add one curl line to your Lambda, ECS task, or EC2 cron job and get alerted by email or Slack if it stops running. No CloudWatch Alarms to configure. No metric filters to maintain. $9/month early access.