Moving scheduled tasks off a cron box and onto serverless functions solves the "one server, one crontab, one single point of failure" problem. It also introduces a quieter one: a job that used to fail loudly in a log you'd eventually read now fails silently, retries in ways you didn't design, and sometimes runs twice. The reliability work doesn't disappear when you delete the crontab — it moves into your function code and your queue configuration, and most teams discover this the first time a nightly billing job double-charges someone.

This is the follow-up to migrating scheduled tasks to serverless: not how to move the jobs, but how to run them without getting paged. The three things that actually matter are delivery semantics (at-least-once, almost always), idempotency (so at-least-once is safe), and observability (so you find out a job didn't run, not just when it errors).

Why does the same job sometimes run twice?

Because nearly every serverless event source is at-least-once, not exactly-once. A scheduled EventBridge rule, an SQS message, a Pub/Sub push, an Azure Queue trigger — all of them can deliver the same event more than once. This isn't a bug you can configure away; it's a property of distributed message delivery. A network blip between the broker and your function acknowledgment means the broker didn't hear "I got it," so it redelivers.