The Silent Failure Problem
Web applications have excellent visibility into request failures — HTTP status codes, error tracking services, and uptime monitors all catch problems quickly. Cron jobs have none of this built-in. A scheduled task that fails silently can go unnoticed for days, corrupting data, missing deadlines, or leaving cleanup tasks undone.
Monitoring scheduled tasks requires intentional instrumentation. This guide covers the key signals to track, how to detect missed runs, and how to get alerted when something goes wrong.
What to Monitor
1. Did the Job Run?
The most fundamental check: did the job start at the expected time? A job that doesn't run at all leaves no logs, no errors — just absence.
Track job start times against the expected schedule. If a job is scheduled for 0 2 * * * and no execution started between 2:00 AM and 2:05 AM, that's a missed run.
# With PandaStack CLI, list executions with timestamps
panda cronjob executions nightly-backup
# Example output
ID Started At Duration Status
exec-8842 2026-05-01 02:00:03 45s SUCCESS
exec-8801 2026-04-30 02:00:01 43s SUCCESS
exec-8762 2026-04-29 02:00:02 47s SUCCESS2. Did the Job Succeed?
A job that ran but exited with a non-zero status code failed. Track exit codes for every run and alert on failures.
# View detailed execution status
panda cronjob executions nightly-backup --format json3. How Long Did It Take?
Duration is a useful signal. A job that normally runs in 30 seconds but suddenly takes 10 minutes may be processing a much larger dataset, hitting a slow query, or waiting on an unavailable dependency.
Track duration percentiles over time — p50, p95, p99. Alert when duration significantly exceeds the historical baseline.
4. Resource Usage
A job consuming 10× its normal CPU or memory may indicate a runaway loop, a data volume anomaly, or a memory leak. Resource limits prevent runaway jobs from affecting other workloads.
Setting Up Heartbeat Monitoring
Heartbeat monitoring (also called dead man's switch monitoring) works by having the job send a "ping" to an external service on successful completion. If the ping doesn't arrive within the expected window, the service alerts you.
#!/bin/bash
# job.sh — include a heartbeat ping on success
set -e
echo "Starting data sync..."
python sync.py
echo "Sync complete — sending heartbeat"
curl -fsS --retry 3 "https://your-monitoring-service/ping/your-job-id" > /dev/nullHeartbeat monitoring catches two failure modes standard log monitoring misses:
- Job didn't run: The scheduler failed, the container didn't start, or the host was down.
- Job ran but failed silently: An exception was caught internally and the job exited 0 without completing its work.
Structured Logging for Cron Jobs
Structured logs make it easy to search, filter, and analyze job behavior across many runs.
import logging
import json
import time
import os
logging.basicConfig(level=logging.INFO, format='%(message)s')
def log(event, **kwargs):
logging.info(json.dumps({
"event": event,
"job_name": os.environ.get("JOB_NAME", "unknown"),
"run_id": os.environ.get("RUN_ID", "unknown"),
"timestamp": time.time(),
**kwargs
}))
def run_job():
log("job_start")
start = time.time()
try:
records = fetch_records()
log("records_fetched", count=len(records))
process_records(records)
log("job_complete", duration_s=round(time.time() - start, 2), records=len(records))
except Exception as e:
log("job_error", error=str(e), duration_s=round(time.time() - start, 2))
raiseStreaming Logs with PandaStack
PandaStack streams logs from container executions in real time. Access them during a run or after completion:
# Stream logs from the currently running job
panda cronjob logs nightly-backup --follow
# View logs from the most recent completed run
panda cronjob logs nightly-backup --latest
# View logs from a specific execution
panda cronjob logs nightly-backup --execution exec-8842The dashboard at [dashboard.pandastack.io](https://dashboard.pandastack.io) shows the full execution history for every cronjob, including start time, duration, exit status, and log output — all in one place.
Alerting on Failures
Alerts should fire when:
- A job fails: Exit code non-zero.
- A job misses its schedule: Expected execution didn't start within a grace period.
- A job times out: Execution exceeded the configured maximum duration.
- A job fails repeatedly: N consecutive failures suggest a systemic problem.
# Example: test your job locally and capture the exit code
docker run --rm \
-e DATABASE_URL=$DATABASE_URL \
your-registry/nightly-backup:latest
if [ $? -ne 0 ]; then
echo "Job failed! Exit code: $?"
# Send alert via your notification system
curl -X POST https://hooks.slack.com/your-webhook \
-H 'Content-type: application/json' \
-d '{"text": "nightly-backup job FAILED"}'
fiMonitoring Checklist
Use this checklist for every scheduled job you deploy to production:
- [ ] Execution history is recorded and queryable
- [ ] Failure exits with non-zero code (don't swallow exceptions)
- [ ] Structured logs include job name, run ID, record counts, and duration
- [ ] Heartbeat monitoring pings on successful completion
- [ ] Duration baselines established and alerts configured for significant deviation
- [ ] Timeout configured to prevent runaway executions
- [ ] On-call alerts configured for repeated failures
Summary
Monitoring cron jobs requires proactive instrumentation — structured logs, heartbeat pings, execution history tracking, and failure alerts. PandaStack provides execution history and real-time log streaming out of the box for every scheduled container job. Visit [docs.pandastack.io](https://docs.pandastack.io) to learn more about PandaStack's cronjob platform.