Scheduled Job Inventory: Find Every Cron Job You Run
Updated
Contents
To find every scheduled job you run, list each place a schedule can live, read each one with that scheduler's own read-only command, and merge the results into one inventory that records each job's owner, schedule, time zone, location, dependents, and whether anything monitors it. On Linux hosts that means every user's crontab, the system crontabs, the periodic directories, anacron, at, and systemd timers. Beyond the hosts, it means Task Scheduler, launchd, Kubernetes, cloud schedulers, CI, application frameworks, and databases. Do it before a migration, an audit, or setting up monitoring: a monitor knows only the jobs that report to it, so the job nobody listed is the one nothing watches.
Every command below is read-only; the docs and man pages behind them were checked on October 25, 2026.
Linux Hosts: Crontabs, Periodic Scripts, and Timers#
crontab -l shows one user's table. A host's full list comes from all of these:
| Where | List it with | Watch for |
|---|---|---|
| User crontabs | sudo ls /var/spool/cron/crontabs (Debian, Ubuntu), /var/spool/cron (cronie: Fedora, RHEL, Arch), /etc/crontabs (BusyBox crond, Alpine) |
One file per user, readable by root (Debian crontab(1), cronie crontab(1)); sudo crontab -l -u alice prints one |
| System crontabs | /etc/crontab, /etc/cron.d/* |
A sixth field names the user. BusyBox crond reads neither (crond.c) |
| Periodic scripts | run-parts --test on each /etc/cron.* directory, or /etc/periodic/* on Alpine |
Prints only what run-parts would run; what triggers each directory differs by distribution |
| anacron | /etc/anacrontab |
Runs the periodic directories on cronie (anacrontab) and Debian (anacrontab) |
| at jobs | sudo atq, then sudo at -c 12 to print job 12 |
Run by root, atq lists everybody's jobs (at(1), at 3.2.5) |
| System timers | systemctl list-timers --all; systemctl list-unit-files --type=timer |
Without --all, loaded but inactive timers are hidden (systemctl-list-units.c); list-unit-files adds installed timers that aren't loaded (systemctl(1)) |
| User timers | ls /var/lib/systemd/linger; sudo systemctl --user [email protected] list-timers --all; find /home/*/.config/systemd/user -name '*.timer' |
A lingering user's manager runs timers with nobody logged in (loginctl(1)); [email protected] needs systemd 248+ (NEWS) |
The crontab guide has a script that prints all of this for one host, systemd timers covers user timers and lingering, and cron security covers hashing these files when the inventory doubles as a persistence audit.
Collect a Fleet's Crontabs and Timers Into One CSV#
This script runs a read-only POSIX sh collector on each host over SSH and writes one CSV row per crontab entry, periodic script, anacron job, and systemd timer.
#!/usr/bin/env bash
# collect-schedules.sh: list crontab entries and systemd timers on Linux hosts as CSV.
# Usage: ./collect-schedules.sh hosts.txt > schedules.csv
# Needs SSH and passwordless sudo on each host: other users' crontabs are root-only.
set -uo pipefail
hosts=${1:?usage: collect-schedules.sh HOSTS_FILE}
# Runs on each host. Prints tab-separated records: kind, source, owner, data.
IFS= read -r -d '' remote <<'REMOTE'
if [ -L /etc/localtime ]; then
zone=$(readlink /etc/localtime); zone=${zone#*zoneinfo/}
elif [ -e /etc/localtime ]; then
zone=$(cat /etc/timezone 2>/dev/null) || zone=unknown
else
zone=UTC
fi
printf 'zone\t-\t-\t%s\n' "$zone"
tag() { # tag KIND FILE OWNER: print each line of FILE with its labels
while IFS= read -r line || [ -n "$line" ]; do
printf '%s\t%s\t%s\t%s\n' "$1" "$2" "$3" "$line"
done < "$2"
}
seen=
for dir in /var/spool/cron/crontabs /var/spool/cron /etc/crontabs; do
[ -d "$dir" ] || continue
real=$(cd -P "$dir" && pwd)
case " $seen " in *" $real "*) continue ;; esac
seen="$seen $real"
for f in "$dir"/*; do
[ -f "$f" ] || continue
case ${f##*/} in \#* | tmp.* | cron.update) continue ;; esac
tag user "$f" "${f##*/}"
done
done
for f in /etc/crontab /etc/cron.d/*; do
[ -f "$f" ] && tag system "$f" -
done
[ -f /etc/anacrontab ] && tag anacron /etc/anacrontab root
for d in /etc/cron.hourly /etc/cron.daily /etc/cron.weekly /etc/cron.monthly \
/etc/cron.yearly /etc/periodic/*; do
[ -d "$d" ] && run-parts --test "$d" 2>/dev/null | while IFS= read -r s; do
printf 'periodic\t%s\troot\t%s\n' "$d" "$s"
done
done
timers() { # timers OWNER [systemctl options that select the service manager]
owner=$1; shift
systemctl "$@" list-units --type=timer --all --no-legend --plain --no-pager 2>/dev/null |
while read -r unit _; do
when=$(systemctl "$@" show --property=TimersCalendar --property=TimersMonotonic \
--value "$unit" | sed -n 's/^{ \(.*\) ; next_elapse=.*/\1/p' | paste -s -d ';' -)
service=$(systemctl "$@" show --property=Unit --value "$unit")
run_as=$(systemctl "$@" show --property=User --value "$service")
printf 'timer\t%s\t%s\t%s\t%s\n' "$unit" "${run_as:-$owner}" "$when" "$service"
done
}
if [ -d /run/systemd/system ]; then
timers root
for u in $(ls /var/lib/systemd/linger 2>/dev/null); do
timers "$u" --user --machine="[email protected]"
done
fi
REMOTE
# Turns the records into CSV rows, quoting every field.
IFS= read -r -d '' parse <<'AWK'
BEGIN { FS = "\t"; OFS = "," }
function q(s) { gsub(/"/, "\"\"", s); return "\"" s "\"" }
function row(kind, src, run_as, sched, cmd) {
print q(host), q(kind), q(src), q(run_as), q(sched), q(zone), q(tz[src]), q(cmd)
}
{
kind = $1; src = $2; owner = $3; rest = $0
for (i = 0; i < 3; i++) rest = substr(rest, index(rest, "\t") + 1)
}
kind == "zone" { zone = rest; next }
kind == "timer" { split(rest, t, "\t"); row("systemd timer", src, owner, t[1], t[2]); next }
kind == "periodic" { row("run-parts", src, owner, src, rest); next }
{
line = rest
sub(/^[ \t]+/, "", line)
if (line == "" || substr(line, 1, 1) == "#") next
if (line ~ /^[A-Za-z_][A-Za-z0-9_]*[ \t]*=/) { # VAR=value line
if (line ~ /^CRON_TZ[ \t]*=/) { v = line; sub(/^CRON_TZ[ \t]*=[ \t]*/, "", v); tz[src] = v }
next
}
split(line, f, /[ \t]+/)
if (kind == "anacron") {
n = 3; sched = "anacron period " f[1]; run_as = "root"
} else {
n = (substr(line, 1, 1) == "@") ? 1 : 5
sched = f[1]; for (i = 2; i <= n; i++) sched = sched " " f[i]
run_as = owner
if (kind == "system") { run_as = f[n + 1]; n++ }
}
cmd = line
for (i = 0; i < n; i++) sub(/^[^ \t]+[ \t]+/, "", cmd)
row(kind == "anacron" ? "anacron" : "cron", src, run_as, sched, cmd)
}
AWK
echo '"host","kind","source","run_as","schedule","host_zone","cron_tz","command"'
while read -r host || [ -n "$host" ]; do
case $host in '' | \#*) continue ;; esac
if ! ssh -o BatchMode=yes -o ConnectTimeout=10 "$host" 'sudo -n sh -s' <<< "$remote" |
awk -v host="$host" "$parse"; then
echo "collect-schedules: could not read every schedule on $host" >&2
fi
done < "$hosts"In the CSV:
host_zoneis the system zone from/etc/localtime.cron_tzis aCRON_TZ=line above the entry, which only cronie schedules by (crontab(5)).- Timer schedules are systemd's normalized form, such as
OnCalendar=*-*-* 06:00:00, in the system zone unless the expression names one (systemd.time(7)). Reading them needs systemd 230 or later, forshow --value. - Not covered: at jobs, timers of users without lingering, and cron inside container images. Add those by hand.
Windows Task Scheduler and macOS launchd#
Get-ScheduledTask "lists all scheduled tasks, regardless of whether they're enabled or disabled" (Microsoft Learn), and -CimSession takes a remote computer name (Get-ScheduledTask). schtasks /query adds each task's advanced properties with /v, writes CSV with /fo csv, and reads another computer with /s, a form Microsoft suggests for tracking tasks across machines (schtasks query):
# Every task outside Microsoft's own \Microsoft\ folders, enabled or not
Get-ScheduledTask | Where-Object TaskPath -notlike '\Microsoft\*' |
Select-Object TaskPath, TaskName, State
# All tasks on app01 with advanced properties, as CSV
schtasks /query /s app01 /fo csv /v > app01-tasks.csvWindows Task Scheduler monitoring covers task history, Last Run Result codes, and alerts for the tasks you find.
On macOS, a launchd job runs periodically when its property list has StartInterval or StartCalendarInterval; the lists live in /Library/LaunchDaemons, /Library/LaunchAgents, each user's ~/Library/LaunchAgents, and Apple's own /System/Library folders (Apple). Plists can be binary, so convert before searching. cron is "still supported" but deprecated (Apple), so check crontab -l too.
# In bash, as root: scheduled launchd jobs outside /System
for f in /Library/LaunchDaemons/*.plist /Library/LaunchAgents/*.plist \
/Users/*/Library/LaunchAgents/*.plist; do
plutil -convert xml1 -o - "$f" 2>/dev/null |
grep -qE '<key>Start(Calendar)?Interval</key>' && echo "$f"
doneKubernetes CronJobs#
kubectl get cronjobs -A covers every namespace of one cluster (kubectl get), so loop over your contexts. Record timeZone: without it, the schedule follows kube-controller-manager's local zone (the field is stable since Kubernetes 1.27). A CronJob with suspend: true stays listed, but the controller starts no new Jobs (CronJob).
for ctx in $(kubectl config get-contexts -o name); do
kubectl --context "$ctx" get cronjobs -A --no-headers \
-o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,SCHEDULE:.spec.schedule,TZ:.spec.timeZone,SUSPEND:.spec.suspend' |
awk -v c="$ctx" '{ print c, $0 }'
done
# Scheduling kinds that operators add
kubectl api-resources | grep -i cronThat finds kinds such as Argo Workflows' CronWorkflow. Kubernetes CronJob monitoring covers what to watch once they're listed.
Cloud Schedulers: AWS, Google Cloud, and Azure#
Cloud schedules are regional. On AWS (CLI 2.38), scheduled rules are a legacy feature that exists only on the default event bus and runs in UTC; AWS recommends EventBridge Scheduler instead (scheduled rules). list-schedules doesn't return expressions, so ask get-schedule for each (list-schedules, get-schedule). Google's jobs list reads one location, the App Engine app's by default (jobs list). Run both per account or project:
# AWS: scheduled rules and Scheduler schedules in every region enabled for the account
for region in $(aws ec2 describe-regions --query 'Regions[].RegionName' --output text); do
aws events list-rules --region "$region" \
--query 'Rules[?ScheduleExpression].[Name,ScheduleExpression,State]' --output text |
awk -v r="$region" '{ print r, "rule", $0 }'
aws scheduler list-schedules --region "$region" \
--query 'Schedules[].[GroupName,Name]' --output text |
while read -r group name; do
aws scheduler get-schedule --region "$region" --group-name "$group" --name "$name" \
--query '[ScheduleExpression,ScheduleExpressionTimezone,State,Target.Arn]' \
--output text < /dev/null |
awk -v r="$region" -v s="$group/$name" '{ print r, "schedule", s, $0 }'
done
done
# Google Cloud: Cloud Scheduler jobs in every location of the current project
for location in $(gcloud scheduler locations list --format='value(locationId)'); do
gcloud scheduler jobs list --location="$location" \
--format='csv[no-heading](name.basename(),schedule,timeZone,state)'
doneOn Azure, search Logic Apps templates for the Recurrence trigger, "type": "Recurrence" in a workflow definition (Recurrence). Functions timer triggers live in code, in a different form per language (timer trigger):
grep -rnE 'TimerTrigger|"timerTrigger"|app\.timer\(|timer_trigger' \
--include='*.cs' --include='*.java' --include='function.json' \
--include='*.js' --include='*.ts' --include='*.py' .A schedule written as %SettingName% comes from an app setting. An AzureWebJobs.<FunctionName>.Disabled setting turns a function off, and deployment slots run their own copies; Microsoft notes it's common to disable timer functions in slots to prevent simultaneous executions (disable functions). The Azure Functions timer trigger guide covers how those schedules run and how to alert on a missed one.
CI Schedules: GitHub Actions and GitLab#
A GitHub Actions schedule is an on.schedule entry in a workflow file. It runs in UTC unless it sets timezone, only from the default branch, and is disabled automatically in a public repository after 60 days without activity (schedule event). GitHub Actions scheduled workflows covers why those runs start late or get dropped. Search each clone's default branch, then check each workflow's state:
grep -lE '^[[:space:]]*schedule:' */.github/workflows/*.y*ml
gh workflow list --all --json name,path,state -R acme/billing-serviceGitLab keeps pipeline schedules outside the repository. GET /projects/:id/pipeline_schedules returns each one's cron, cron_timezone, next_run_at, active, and owner, and the API documents only this per-project list (pipeline schedules), so loop over projects.
Schedules Defined in Application Code#
Framework schedulers keep registries you can read from inside the application. For Hangfire 1.8, read the recurring-job registry (RecurringJobDto); Hangfire monitoring covers the rest of its storage API:
using Hangfire;
using Hangfire.Storage;
using var connection = JobStorage.Current.GetConnection();
foreach (var job in connection.GetRecurringJobs())
{
var target = job.Job is null ? "(type not loadable)" : $"{job.Job.Type.Name}.{job.Job.Method.Name}";
Console.WriteLine($"{job.Id}\t{job.Cron}\t{job.TimeZoneId}\t{target}\tnext={job.NextExecution:u}");
}For Quartz.NET 3.x, scheduler.GetJobKeys(GroupMatcher<JobKey>.AnyGroup()) returns every job, GetTriggersOfJob(jobKey) its triggers, and an ICronTrigger carries CronExpressionString and TimeZone (IScheduler, ICronTrigger). List jobs with no trigger too: they run only when something triggers them by hand.
Other frameworks:
# Celery 5.6: the beat_schedule setting (proj.celery is your Celery app)
python -c "from proj.celery import app; print(app.conf.beat_schedule)"
# django-celery-beat 2.9: tasks in the database, including ones added in the admin
python manage.py shell -c "from django_celery_beat.models import PeriodicTask
for t in PeriodicTask.objects.all(): print(t.enabled, t, t.task)"
# Laravel 13: each task and when it is next due
php artisan schedule:list
# Rails with whenever 1.1.4: schedule.rb as crontab lines, without touching the crontab
bundle exec whenever
# Rails with Solid Queue 1.7: tasks loaded from recurring.yml, plus dynamic ones
bin/rails runner 'SolidQueue::RecurringTask.find_each { |t| puts [t.key, t.schedule, t.class_name || t.command, t.static ? "static" : "dynamic"].join("\t") }'
# Spring Boot 4.1 with the scheduledtasks Actuator endpoint exposed, then the source
curl -s http://localhost:8080/actuator/scheduledtasks
grep -rn --include='*.java' --include='*.kt' '@Scheduled' src/mainCelery beat takes its entries from beat_schedule by default, while django-celery-beat keeps the schedule in the Django database (periodic tasks, models.py). Solid Queue rewrites its static task rows from the file whenever its scheduler starts (recurring_schedule.rb) and keeps dynamic tasks only in the database (README). See also Laravel's schedule:list, whenever's README, and Spring Boot's scheduledtasks endpoint. A @Scheduled grep misses tasks registered through a TaskScheduler in code.
Database Schedulers: pg_cron, SQL Server Agent, and MySQL Events#
Databases run schedules that no host or repository shows:
-- pg_cron 1.3+, in the database named by cron.database_name
SELECT jobid, jobname, schedule, command, database, username, active
FROM cron.job
ORDER BY jobid;
-- SQL Server Agent: every job with each of its schedules and the next run
SELECT j.name AS job_name, j.enabled AS job_enabled, SUSER_SNAME(j.owner_sid) AS job_owner,
s.name AS schedule_name, s.enabled AS schedule_enabled, s.freq_type, s.freq_interval,
s.freq_subday_type, s.freq_subday_interval, s.active_start_time,
js.next_run_date, js.next_run_time
FROM msdb.dbo.sysjobs AS j
LEFT JOIN msdb.dbo.sysjobschedules AS js ON js.job_id = j.job_id
LEFT JOIN msdb.dbo.sysschedules AS s ON s.schedule_id = js.schedule_id
ORDER BY j.name;
-- MySQL 8.4: events in every schema the account can see
SELECT EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_TYPE, INTERVAL_VALUE, INTERVAL_FIELD,
EXECUTE_AT, STARTS, ENDS, TIME_ZONE, STATUS, LAST_EXECUTED
FROM information_schema.EVENTS
ORDER BY EVENT_SCHEMA, EVENT_NAME;Each query returns only what its account may see. pg*cron's row-level security limits a role to its own jobs unless it is a superuser or has bypassrls (README), and MySQL shows information_schema rows only for objects the account has privileges on (INFORMATION_SCHEMA). Agent's sysjobschedules refreshes every 20 minutes (sysjobschedules), and sysschedules decodes the freq*\*columns. MySQL events run only whileevent_schedulerisON, the 8.4 default (event scheduler).
SaaS automation tools such as Zapier can run work on a timer too; list those from each tool's own interface.
What Each Inventory Record Needs#
The collected rows say what runs. The inventory adds who owns it and what breaks without it, one row per job:
job_id,scheduler,location,schedule,time_zone,run_as,owner,depends_on,definition,environment,monitor_key,runbook_url,last_seen| Column | Example | Why |
|---|---|---|
job_id |
invoice-export-nightly |
One stable name, reusable as the monitor key |
scheduler, location |
cronie, app03.prod |
Where to look when it stops; what a migration moves |
schedule, time_zone |
30 1 * * *, Europe/Berlin |
The same expression is a different hour in another zone |
run_as |
invoice |
Privileges and secrets; review root jobs first |
owner |
billing-team |
Who gets the alert (cron job alerting) |
depends_on |
finance close report, 09:00 |
What breaks, and by when |
definition |
billing/deploy/cron.d/invoice-export |
Where it's changed; a job defined only on a server is a finding |
environment |
production |
Keeps staging out of production alerts |
monitor_key |
invoice-export-nightly |
Empty means nothing watches it |
runbook_url |
https://git.example.com/billing/runbooks/invoice.md |
What the alert links to (scheduled job runbooks) |
last_seen |
2026-10-25 |
The last scan that found it; an old date means removed or moved |
To give each row a priority class and the rest of a per-job contract, use the CronRadar Scheduled Job Methodology.
Check the Inventory Against What Reports to CronRadar#
In CronRadar, two .NET extensions build their part of the monitor list themselves (package versions checked September 20, 2026):
- CronRadar.Hangfire 0.1.0:
builder.Services.AddCronRadarHangfire();afterAddHangfire()reads the recurring-job registry at startup and reconciles it every 15 minutes, monitoring every recurring job whose method lacks[SkipMonitor]. When a job leaves the registry, CronRadar pauses its monitor and marks it as no longer discovered, keeping its history. This release can report a failure before Hangfire finishes retrying, including cancellations; reporting only after retries are exhausted needs the pending 0.2.1 (Hangfire integration). - CronRadar.Quartz 0.1.0:
await scheduler.MonitorAll();after registering jobs and beforeStart()walks every registered job and trigger;[SkipMonitor]on anIJobclass opts out. 0.1.0 sends a cron trigger's six-field expression unchanged, and CronRadar accepts only five-field cron, so until the pending 0.1.1 only jobs on repeating simple triggers are monitored, with approximate five-field schedules (* * * * *below a minute). It covers Quartz.NET 3.x (3.4+), not 4.x; on 4.x, call the base SDK'sCronRadar.CronRadar.WrapAsyncinside the job, fully qualified because the class shares its namespace's name (Quartz.NET integration).
A discovered monitor stays pending until the first run, and CronRadar sends a never-executed alert if its first expected run plus the grace period passes with no report. CronRadar evaluates schedules in UTC, so for a job on another zone, compare CronRadar's next expected run with the scheduler's next fire time.
Nothing else is discovered, and outside .NET there is no installable CronRadar SDK: crontab, systemd, Task Scheduler, Kubernetes, cloud, CI, database, and other framework jobs each report with their own HTTP check-in to https://cron.life/api/v1/ping/{monitor_key}, and .NET code can use the base CronRadar SDK. A check-in that carries schedule creates its monitor, so a job that never runs never appears; to catch that, create the monitor in the dashboard before the first run. Copy owner and runbook_url into the monitor's "Owner or team" and "Runbook URL" settings: the owner appears in email, Slack, Discord, PagerDuty, and webhook alerts, the runbook link in webhook payloads and PagerDuty custom details.
Then compare the lists. GET /api/v1/sync/status returns one application's monitors with their keys and pause state (API reference). With one application per environment, run this with each environment's key:
#!/usr/bin/env bash
# inventory-vs-monitors.sh: compare inventory rows with one CronRadar application's monitors.
# Usage: CRONRADAR_API_KEY=... ./inventory-vs-monitors.sh inventory.csv production
set -euo pipefail
inventory=${1:?usage: inventory-vs-monitors.sh INVENTORY_CSV ENVIRONMENT}
environment=${2:?usage: inventory-vs-monitors.sh INVENTORY_CSV ENVIRONMENT}
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
python3 - "$inventory" "$environment" > "$tmp/rows" <<'EOF'
import csv, sys
with open(sys.argv[1], newline="") as f:
for row in csv.DictReader(f):
if row["environment"] == sys.argv[2]:
print(row["monitor_key"] or "-", row["job_id"])
EOF
curl --fail --silent --show-error --max-time 10 --user "$CRONRADAR_API_KEY:" \
https://cron.life/api/v1/sync/status > "$tmp/status.json"
awk '$1 == "-" { print "UNMONITORED", $2 }' "$tmp/rows"
awk '$1 != "-" { print $1 }' "$tmp/rows" | sort -u > "$tmp/expected"
jq -r '.monitors[].monitorKey' "$tmp/status.json" | sort -u > "$tmp/monitors"
comm -23 "$tmp/expected" "$tmp/monitors" | sed 's/^/NO MONITOR /'
comm -13 "$tmp/expected" "$tmp/monitors" | sed 's/^/NOT IN INVENTORY /'
jq -r '.monitors[] | select(.isPaused) | "PAUSED \(.monitorKey)"' "$tmp/status.json"UNMONITORED and NO MONITOR are jobs nothing watches yet. NOT IN INVENTORY is a job that reports but was never recorded. PAUSED covers monitors paused by hand, held by a plan limit, or paused because a discovered job left its registry.
Keep the Inventory Current#
- Re-scan on a schedule. Run the collectors weekly, update
last_seen, and diff each scan against the last. Give the collector its own check-in, so one that stops doesn't leave an old inventory looking current. - Check in CI. Fail a pull request that adds a schedule, such as a workflow
schedule:, arecurring.ymlkey, an@Scheduledmethod, or a CronJob manifest, without an inventory row. - Diff against monitoring after each scan. The Hangfire and Quartz.NET 3.x extensions keep their side current; every other row needs its check-in added by hand.
Frequently Asked Questions#
How do I list all cron jobs on a Linux server?#
As root, read every file in the cron spool (/var/spool/cron/crontabs on Debian and Ubuntu, /var/spool/cron on cronie systems, /etc/crontabs on Alpine), plus /etc/crontab, /etc/cron.d, the periodic directories, and /etc/anacrontab. Then add systemctl list-timers --all and atq; crontab -l alone shows only the current user.
How do I find every scheduled task across servers?#
Run the same read-only collection on every host over SSH and merge the output, as the fleet script above does for Linux; on Windows, schtasks /query /s or Get-ScheduledTask -CimSession reads remote machines. Then add what isn't on any host: Kubernetes, cloud schedulers in every region, CI schedules, framework registries, and database schedulers.
How do I list systemd timers?#
systemctl list-timers --all lists system timers, including loaded but inactive ones, with their next and last runs. systemctl --user list-timers --all lists your own, and as root, systemctl --user [email protected] list-timers --all lists alice's on systemd 248 or later while her user manager runs.
How do I find scheduled jobs in Kubernetes?#
Run kubectl get cronjobs -A against every context in your kubeconfig and record each CronJob's timeZone and suspend fields. kubectl api-resources | grep -i cron finds scheduling kinds added by operators, such as Argo Workflows' CronWorkflow.
Can CronRadar find my scheduled jobs automatically?#
Only through two .NET extensions: CronRadar.Hangfire 0.1.0 reconciles Hangfire's recurring-job registry, and CronRadar.Quartz 0.1.0 declares every job when the scheduler starts on Quartz.NET 3.x, though CronRadar accepts only jobs on repeating simple triggers until the pending 0.1.1. Crontab, systemd, Kubernetes, cloud schedulers, and other frameworks report through an HTTP check-in per job, so the monitor list is only as complete as what reports. Build the inventory first, then compare it with the monitor list.
Put this guide to work
Know when the next run is missing
Connect one scheduled job, confirm a real completion, then verify the alert destination. Keep your existing scheduler in charge of running the work.