Supabase Scheduled Functions Monitoring: How to Catch Missed Runs Before They Break Production

Supabase scheduled functions monitoring matters because scheduled backend work can fail quietly while your app still looks completely healthy.

Your frontend loads. Your API responds. The database is online. Auth works. But the scheduled function that cleans old rows, syncs billing data, sends reminders, refreshes materialized views, or calls an external API may have stopped running hours ago.

That is the dangerous part about scheduled work in serverless and database-backed apps: the failure is often invisible until some downstream symptom appears.

A scheduled function is not “up” in the same way a web endpoint is up. It either ran when expected and completed the important work, or it did not.

Supabase gives developers a powerful stack for building quickly, including Edge Functions, Postgres, cron-like scheduling patterns, and database automation. But once scheduled tasks become part of production, you need monitoring that answers a very specific question:

Did this scheduled function actually run and finish successfully?

The problem

Scheduled backend work is easy to add and easy to forget.

In a Supabase app, you might have scheduled work that:

  • deletes expired sessions or temporary records
  • sends daily or weekly email digests
  • refreshes reporting tables
  • syncs subscription status from a payment provider
  • calls an external API on a schedule
  • checks usage limits
  • exports analytics
  • cleans up old files in storage
  • recalculates account metrics
  • triggers notifications
  • verifies backup or data consistency jobs

Some of this work may live in Supabase Edge Functions. Some may be driven by Postgres cron extensions, database triggers, external schedulers, GitHub Actions, Vercel Cron, or another service that calls a Supabase function endpoint.

The exact implementation varies, but the operational risk is the same:

If the scheduled job does not run, your main app can still look fine.

Traditional uptime monitoring might check your homepage or API route. That is useful, but it does not tell you whether yesterday’s cleanup ran, whether today’s digest was sent, or whether the scheduled sync completed.

This creates a silent failure.

The system is not fully down. There may be no obvious error on the public surface. But an important background process is missing.

Why it happens

Supabase scheduled functions can fail silently for several reasons.

One common cause is scheduler drift. The schedule may be configured outside the main application code, or it may depend on infrastructure that someone rarely checks. A cron expression can be wrong, disabled, duplicated, or moved to a different environment. A staging schedule may accidentally replace production behavior, or production may stop receiving triggers after a deployment change.

Another cause is function deployment drift. Edge Functions are code, and code changes. A function can be renamed, removed, redeployed with different environment variables, or changed in a way that breaks scheduled execution while manual tests still pass.

For example, a scheduled function might depend on an environment variable:

const apiKey = Deno.env.get("EXTERNAL_API_KEY");

if (!apiKey) {
  throw new Error("Missing EXTERNAL_API_KEY");
}

If that secret is missing in production, the function may fail every time the scheduler invokes it.

Database permissions can also be a problem. Scheduled work often touches tables, service-role operations, storage buckets, or external APIs. A permission change that is safe for normal user requests can still break a privileged background job.

External dependencies are another source of failure. A scheduled function might call Stripe, Resend, Slack, OpenAI, a webhook endpoint, or an internal API. If that dependency times out, rate limits requests, changes response shape, or rejects credentials, the scheduled job can fail even though Supabase itself is healthy.

Timeouts and partial completion are especially tricky. A function might start successfully, process half the records, then time out. Logs may contain the failure, but unless someone checks them or receives an alert, the job can keep failing silently.

Finally, many teams confuse logs with monitoring. Supabase logs are useful when investigating a known issue. But logs do not automatically prove that a scheduled job ran on time and completed successfully.

Monitoring should detect the missing success signal before a human goes digging through logs.

Why it’s dangerous

Missed scheduled functions are dangerous because they usually manage work that users do not directly trigger.

A failed scheduled function can cause:

  • stale reports or dashboards
  • expired data staying in the database
  • email digests not being sent
  • payment status not syncing
  • usage limits not updating
  • notification queues backing up
  • old files accumulating in storage
  • cleanup tasks never running
  • third-party integrations falling behind
  • delayed or incorrect customer-facing data

These failures often compound.

If a daily cleanup misses one run, maybe nothing obvious happens. If it misses seven runs, tables grow unexpectedly, queries slow down, storage costs rise, and users start seeing outdated data.

If a billing sync stops, customers may keep access after cancellation, lose access after payment, or receive confusing account states.

If a reporting refresh fails, business dashboards quietly become wrong. That can lead to bad decisions because the data looks normal, just stale.

The painful part is that the first visible symptom usually appears far away from the root cause.

Someone might report:

  • “Why didn’t I get the digest?”
  • “Why is this dashboard stale?”
  • “Why is this user still marked active?”
  • “Why is storage growing so fast?”
  • “Why did this webhook not update the account?”

Then you have to reconstruct what happened:

  • Did the scheduler fire?
  • Did Supabase receive the request?
  • Did the Edge Function start?
  • Did it have the right secrets?
  • Did it finish?
  • Did it fail halfway through?
  • Did it retry?
  • Did anyone get alerted?

That is exactly the kind of uncertainty good scheduled function monitoring should remove.

How to detect it

The most reliable way to detect missed scheduled functions is to make the function send a success signal after the important work completes.

This is heartbeat monitoring.

Instead of asking, “Is my Supabase project online?”, heartbeat monitoring asks:

“Did this specific scheduled function report success inside the expected time window?”

The pattern is simple:

  1. Create a heartbeat check for the expected schedule.
  2. Run your scheduled Supabase function normally.
  3. Send a heartbeat ping only after the critical work succeeds.
  4. Alert if the ping does not arrive on time.

This catches the failure mode that logs and uptime checks often miss: absence.

If the scheduler never fires, no ping arrives.

If the function crashes before completion, no ping arrives.

If a secret is missing, no ping arrives.

If an external API fails and the job exits early, no ping arrives.

If the job hangs or times out before reaching the final step, no ping arrives.

That makes the monitoring signal much more meaningful than checking whether a generic endpoint returns 200 OK.

The heartbeat should represent successful completion, not just startup.

For example, if your function syncs subscription status, the ping should happen after the sync finishes. If your function sends a daily digest, the ping should happen after the digest job completes. If your function refreshes reporting tables, the ping should happen after the refresh succeeds.

A startup ping only proves that the function began. A completion ping proves the work finished.

Simple solution with example

Here is a simplified Supabase Edge Function example.

Imagine you have a scheduled function that cleans up expired rows once per day.

// supabase/functions/cleanup-expired-records/index.ts

import { serve } from "https://deno.land/std@0.224.0/http/server.ts";
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";

serve(async () => {
  const supabaseUrl = Deno.env.get("SUPABASE_URL");
  const serviceRoleKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY");
  const heartbeatUrl = Deno.env.get("CLEANUP_HEARTBEAT_URL");

  if (!supabaseUrl || !serviceRoleKey) {
    return new Response("Missing Supabase configuration", { status: 500 });
  }

  const supabase = createClient(supabaseUrl, serviceRoleKey);

  const { error } = await supabase
    .from("temporary_records")
    .delete()
    .lt("expires_at", new Date().toISOString());

  if (error) {
    console.error("Cleanup failed:", error);
    return new Response("Cleanup failed", { status: 500 });
  }

  if (heartbeatUrl) {
    const ping = await fetch(heartbeatUrl);

    if (!ping.ok) {
      console.error("Heartbeat ping failed:", ping.status);
      return new Response("Heartbeat failed", { status: 500 });
    }
  }

  return new Response("Cleanup completed", { status: 200 });
});

Then set the heartbeat URL as an environment variable:

CLEANUP_HEARTBEAT_URL=https://quietpulse.xyz/ping/{token}

The exact scheduler can vary. You might call this Edge Function from an external cron service, from GitHub Actions, from another platform scheduler, or from a database-driven scheduling setup.

The important part is not which scheduler triggers the function.

The important part is that the function reports success only after the critical work completes.

If the scheduled function runs every day at 02:00 UTC, configure the heartbeat check to expect one ping per day, with a reasonable grace period.

For example:

  • expected interval: 24 hours
  • grace period: 30–60 minutes
  • alert channel: Telegram, webhook, or another notification route

That way, if the function does not complete by the expected time, you get an alert.

Instead of discovering the issue from stale data later, you know shortly after the scheduled work fails to report success.

Instead of building this alerting flow yourself, you can use a heartbeat monitoring tool like QuietPulse. Create a check, copy the ping URL, and call it at the end of your scheduled function. If the ping does not arrive on time, QuietPulse can notify you through Telegram or webhooks.

Common mistakes

1. Pinging at the start of the function

A heartbeat ping at the beginning only proves that the function started.

That can create false confidence.

If the function starts, deletes nothing, fails on an external API call, times out, or crashes halfway through, the monitor still sees a successful ping.

For scheduled functions, the heartbeat should usually be the final step after the important work completes.

2. Monitoring only the public app

A homepage uptime check is useful, but it does not monitor background work.

Your Supabase app can be online while scheduled functions fail for days.

Use uptime checks for request/response availability. Use heartbeat checks for scheduled work.

They answer different questions.

3. Relying only on logs

Logs are valuable for debugging, but they are weak as the first line of detection.

If nobody is watching the logs, a failure can sit there unnoticed.

A heartbeat check gives you an explicit missing-success alert. Logs then help you investigate why the success signal did not arrive.

4. Using one heartbeat for multiple jobs

If you have several scheduled functions, do not hide them behind one generic monitor.

A cleanup job, billing sync, digest sender, and reporting refresh should usually have separate checks.

Separate checks make alerts actionable. You immediately know which scheduled function missed its expected run.

5. Ignoring partial failures

A function can return success even when part of the work failed.

For example, a digest job might send 900 emails out of 1,000 and silently skip the rest. A sync might process one page of API results and fail before the next page.

Make sure your function treats important partial failures as failures. Only ping the heartbeat after the job meets your real success criteria.

Alternative approaches

Heartbeat monitoring is the cleanest way to detect missed scheduled functions, but it is not the only useful signal.

Supabase logs

Supabase logs are important for debugging. They can show function invocations, errors, stack traces, and timing information.

Use them to answer “what happened?”

But logs are less reliable for answering “did the expected scheduled function finish on time?” unless you build alerting around them.

Database audit tables

Some teams create a job_runs table and insert a row for each scheduled execution.

For example:

create table job_runs (
  id uuid primary key default gen_random_uuid(),
  job_name text not null,
  status text not null,
  started_at timestamptz not null default now(),
  finished_at timestamptz,
  error_message text
);

This can be very useful, especially for internal dashboards and debugging history.

But you still need something to check that table and alert when a run is missing or failed. Otherwise, it becomes another place where failures are recorded but not noticed.

External scheduler alerts

Some schedulers provide failure notifications. That helps when the scheduler fires and receives a failing response.

But scheduler alerts may not catch every important case. They might not know whether the function completed all internal work correctly. They also may not alert when the function returns 200 OK too early.

Heartbeat monitoring works well alongside scheduler alerts because it focuses on completion of the actual work.

Application metrics

Metrics are useful when scheduled functions affect measurable values: rows processed, emails sent, API records synced, duration, error count, and so on.

If you already have metrics infrastructure, instrumenting scheduled functions is a good idea.

But for many small teams and indie projects, a simple heartbeat is faster to set up and catches the most important failure mode: the job did not complete.

FAQ

What is Supabase scheduled functions monitoring?

Supabase scheduled functions monitoring is the practice of tracking whether scheduled backend work in a Supabase app runs and completes successfully. This can include Edge Functions, database jobs, cleanup tasks, sync jobs, reporting refreshes, and other recurring automation.

How do I know if a Supabase scheduled function did not run?

The most direct way is to use a heartbeat check. Add a ping at the end of the scheduled function after the important work succeeds. If the ping does not arrive within the expected time window, the function probably did not run or did not complete successfully.

Are Supabase logs enough for scheduled function monitoring?

Supabase logs are useful for debugging, but they are not always enough for monitoring. Logs can show errors after you know there is a problem. Heartbeat monitoring alerts you when the expected success signal is missing.

Should I ping before or after the scheduled function work?

Usually after. A heartbeat ping should represent successful completion. If you ping at the beginning, the function can still fail halfway through while the monitor thinks everything is fine.

Can I monitor multiple Supabase scheduled functions with one heartbeat?

You can, but it is usually better to create one heartbeat check per important scheduled function. Separate checks make alerts clearer and help you quickly identify which job missed its run.

Conclusion

Scheduled functions are easy to trust because they usually run quietly in the background.

That quietness is also the risk.

A Supabase app can look healthy while a cleanup job, billing sync, digest sender, or reporting refresh silently stops running. Uptime checks and logs help, but they do not always prove that the scheduled work completed on time.

For production scheduled work, add a completion signal.

Use heartbeat monitoring to confirm that each important Supabase scheduled function runs when expected and finishes successfully. If the heartbeat does not arrive, alert early, investigate quickly, and fix the issue before stale data or missed automation turns into a real incident.

Originally published at https://quietpulse.xyz/blog/supabase-scheduled-functions-monitoring

Total
0
Shares
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post

Why you can never get your doctor to call you back

Related Posts