Introduction

Kubernetes CronJob misses its scheduled execution time, not creating Jobs when expected. Scheduled tasks don't run, causing automation failures, backup gaps, or monitoring lapses.

Symptoms

No Jobs created:

```bash $ kubectl get jobs -l cronjob-name=my-cronjob

No resources found in default namespace.

$ kubectl get cronjob my-cronjob

NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE my-cronjob */5 * * * * False 0 <none> 1h ```

CronJob events show missed:

```bash $ kubectl describe cronjob my-cronjob

Events: Type Reason Age From Message ---- ------ --- ---- ------- Normal Missed 5m cronjob-controller Missed scheduled time for ... ```

Jobs not starting:

```bash $ kubectl logs -l job-name=my-cronjob-*

No pods found for selector ```

Common Causes

  1. 1.Wrong timezone - Schedule uses local time but cluster uses UTC
  2. 2.Concurrency policy blocking - Previous job still running
  3. 3.Starting deadline missed - Too far behind schedule
  4. 4.Cron syntax error - Invalid schedule format
  5. 5.Suspended CronJob - CronJob.spec.suspend=true
  6. 6.Controller issues - CronJob controller not running

Step-by-Step Fix

  1. 1.Check logs for specific error messages
  2. 2.Verify configuration settings
  3. 3.Test network connectivity
  4. 4.Review recent changes
  5. 5.Apply corrective action
  6. 6.Verify the fix

Step 1: Check CronJob Status

```bash # Get CronJob details kubectl get cronjob my-cronjob -o yaml

# Check schedule kubectl get cronjob my-cronjob -o jsonpath='{.spec.schedule}'

# Check if suspended kubectl get cronjob my-cronjob -o jsonpath='{.spec.suspend}'

# Check last schedule time kubectl get cronjob my-cronjob -o jsonpath='{.status.lastScheduleTime}'

# Check active jobs kubectl get cronjob my-cronjob -o jsonpath='{.status.active}'

# Describe CronJob for events kubectl describe cronjob my-cronjob

# Check all jobs from this CronJob kubectl get jobs -l cronjob=my-cronjob ```

Step 2: Verify Cron Schedule Syntax

```bash # Check current schedule kubectl get cronjob my-cronjob -o jsonpath='{.spec.schedule}'

# Cron format: minute hour day month weekday # Examples: # */5 * * * * - Every 5 minutes # 0 * * * * - Every hour # 0 0 * * * - Every day at midnight # 0 0 * * 0 - Every Sunday at midnight # 0 9 * * 1-5 - Weekdays at 9am

# Test cron schedule online or with cron parser # Common mistakes: # 5 * * * * = At minute 5 (wrong if you meant every 5 min) # */5 * * * * = Every 5 minutes (correct)

# Check timezone (Kubernetes uses UTC by default) kubectl get cronjob my-cronjob -o jsonpath='{.spec.timeZone}'

# For Kubernetes 1.27+ with timezone support: spec: timeZone: "America/New_York" schedule: "0 9 * * *" # 9am New York time = different UTC time

# Without timeZone, schedule is UTC # If your server is in EST, "0 9 * * *" UTC = 4am EST ```

Step 3: Check Concurrency Policy

```bash # Check concurrency policy kubectl get cronjob my-cronjob -o jsonpath='{.spec.concurrencyPolicy}'

# Values: # Allow (default) - Multiple jobs can run simultaneously # Forbid - Skip new job if previous still running # Replace - Replace previous job with new one

# If using Forbid, check if previous job still running kubectl get jobs -l cronjob=my-cronjob

# Check job status kubectl describe job my-cronjob-12345

# If previous job stuck, next runs are skipped kubectl get pods -l job-name=my-cronjob-12345

# Fix stuck job kubectl delete job my-cronjob-12345

# Or change concurrency policy kubectl patch cronjob my-cronjob -p '{"spec":{"concurrencyPolicy":"Allow"}}' ```

Step 4: Check Starting Deadline Seconds

```bash # Check starting deadline kubectl get cronjob my-cronjob -o jsonpath='{.spec.startingDeadlineSeconds}'

# If CronJob controller was down, jobs may be missed # startingDeadlineSeconds controls how late a job can start

# If set to 100, and controller resumes after 200 seconds past schedule, # job is skipped (missed deadline)

# Increase starting deadline kubectl patch cronjob my-cronjob -p '{"spec":{"startingDeadlineSeconds":300}}'

# Or remove limit (job will start whenever controller recovers) kubectl patch cronjob my-cronjob --type json -p '[{"op":"remove","path":"/spec/startingDeadlineSeconds"}]'

# Default: no limit, but jobs too far behind are skipped anyway ```

Step 5: Check if CronJob is Suspended

```bash # Check suspend status kubectl get cronjob my-cronjob -o jsonpath='{.spec.suspend}'

# If true, CronJob is suspended # No new jobs will be created

# Unsuspend CronJob kubectl patch cronjob my-cronjob -p '{"spec":{"suspend":false}}'

# Or in yaml: spec: suspend: false

# Apply kubectl apply -f cronjob.yaml

# Verify kubectl get cronjob my-cronjob # SUSPEND should show False ```

Step 6: Check CronJob Controller Health

```bash # CronJob controller runs in kube-controller-manager

# Check kube-controller-manager status kubectl get pods -n kube-system -l component=controller-manager

# Check controller-manager logs kubectl logs -n kube-system kube-controller-manager-xxx | grep cronjob

# Check if controller is running ps aux | grep kube-controller-manager

# For managed Kubernetes (EKS, GKE, AKS), check provider status

# Restart controller-manager (self-managed clusters) kubectl rollout restart deployment kube-controller-manager -n kube-system

# Or static pod approach: mv /etc/kubernetes/manifests/kube-controller-manager.yaml /tmp/ sleep 10 mv /tmp/kube-controller-manager.yaml /etc/kubernetes/manifests/ ```

Step 7: Manual Trigger for Testing

```bash # Create job manually from CronJob template kubectl create job --from=cronjob/my-cronjob manual-job-001

# Check job created kubectl get job manual-job-001

# Check job logs kubectl logs -l job-name=manual-job-001

# If manual job works but scheduled doesn't, check schedule/CronJob controller

# Delete test job kubectl delete job manual-job-001

# Verify CronJob creates next scheduled job kubectl get jobs -w ```

Step 8: Fix Missed Job Execution

```bash # If jobs were missed and you need to catch up

# Option 1: Create manual jobs for missed times kubectl create job --from=cronjob/my-cronjob catchup-job-1 kubectl create job --from=cronjob/my-cronjob catchup-job-2

# Option 2: Adjust schedule temporarily kubectl patch cronjob my-cronjob -p '{"spec":{"schedule":"*/1 * * * *"}}'

# Wait for catch-up, then restore original schedule kubectl patch cronjob my-cronjob -p '{"spec":{"schedule":"*/5 * * * *"}}'

# Option 3: Increase successful jobs history limit kubectl patch cronjob my-cronjob -p '{"spec":{"successfulJobsHistoryLimit":10}}'

# Check history kubectl get jobs -l cronjob=my-cronjob ```

Step 9: Add Proper Timezone Configuration

```yaml # For Kubernetes 1.27+, use timeZone field apiVersion: batch/v1 kind: CronJob metadata: name: my-cronjob spec: timeZone: "America/New_York" # Specify timezone schedule: "0 9 * * *" # 9am in specified timezone jobTemplate: spec: template: spec: containers: - name: job image: job-image

# For older Kubernetes, handle timezone in job script: jobTemplate: spec: template: spec: containers: - name: job image: job-image command: - /bin/sh - -c - | # Convert to local timezone in script TZ=America/New_York current_hour=$(TZ=$TZ date +%H) if [ "$current_hour" -eq 9 ]; then echo "Running at 9am local time" # Run job fi

# Or use environment variable env: - name: TZ value: "America/New_York" ```

Step 10: Monitor CronJob Execution

```bash # Monitor CronJob events kubectl get events --field-selector involvedObject.kind=CronJob -w

# Create monitoring script cat << 'EOF' > monitor_cronjob.sh #!/bin/bash CRONJOB=my-cronjob

echo "=== CronJob Status ===" kubectl get cronjob $CRONJOB

echo "" echo "=== Recent Jobs ===" kubectl get jobs -l cronjob=$CRONJOB --sort-by=.metadata.creationTimestamp | tail -5

echo "" echo "=== Last Job Pod Status ===" LAST_JOB=$(kubectl get jobs -l cronjob=$CRONJOB --sort-by=.metadata.creationTimestamp -o jsonpath='{.items[-1].metadata.name}') kubectl get pods -l job-name=$LAST_JOB

echo "" echo "=== Recent Events ===" kubectl get events --field-selector involvedObject.name=$CRONJOB | tail -10 EOF

chmod +x monitor_cronjob.sh

# Set up alert for missed schedules # In Prometheus/alertmanager: - alert: CronJobMissedSchedule expr: | increase(kube_cronjob_status_missed_schedule_total[5m]) > 0 for: 1m labels: severity: warning annotations: summary: "CronJob {{ $labels.cronjob }} missed schedule" EOF ```

Kubernetes CronJob Schedule Checklist

CheckCommandExpected
Schedule syntaxget cronjob -o yamlValid cron format
Suspend statusjsonpath .spec.suspendfalse
Concurrency policyjsonpath .spec.concurrencyPolicyAppropriate
Last schedule timedescribe cronjobRecent timestamp
Active jobsget jobs -l cronjobRunning jobs
Controller healthget eventsNo errors

Verification

```bash # After fixing CronJob schedule issues

# 1. Check CronJob triggers at correct time kubectl get jobs -l cronjob=my-cronjob -w // Should create job at scheduled time

# 2. Verify job completes kubectl get job my-cronjob-12345 // Should show Completion: 1/1

# 3. Check job logs kubectl logs -l job-name=my-cronjob-12345 // Should show job execution output

# 4. Monitor CronJob events kubectl get events --field-selector involvedObject.kind=CronJob // No missed schedule events

# 5. Verify schedule is correct kubectl get cronjob my-cronjob -o jsonpath='{.spec.schedule}' // Should match intended schedule

# 6. Check timezone if applicable kubectl get cronjob my-cronjob -o jsonpath='{.spec.timeZone}' // Should be correct timezone ```

Prevention

To prevent Kubernetes CronJob missed schedules from recurring, implement these proactive measures:

1. Monitor CronJob Execution

yaml
groups:
- name: kubernetes-cronjob
  rules:
  - alert: KubernetesCronJobMissed
    expr: |
      kube_cronjob_status_last_schedule_time < time() - 3600
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "CronJob {{ $labels.cronjob }} has not run recently"

2. Configure Proper CronJob Settings

yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: scheduled-job
spec:
  schedule: "0 * * * *"
  concurrencyPolicy: Forbid  # Prevent overlapping runs
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 3
  startingDeadlineSeconds: 300  # Allow 5 min delay
  jobTemplate:
    spec:
      backoffLimit: 3
      activeDeadlineSeconds: 600
      template:
        spec:
          restartPolicy: OnFailure

3. Set Up Job Completion Monitoring

```bash # Check for stuck jobs kubectl get jobs -l app=my-cronjob

# Monitor job status kubectl get cronjob my-cronjob -o jsonpath='{.status.lastScheduleTime}' ```

4. Configure Timezone Correctly

yaml
# Use timezone if supported
spec:
  timeZone: "America/New_York"
  schedule: "0 9 * * 1-5"  # 9 AM weekdays EST

Best Practices Checklist

  • [ ] Monitor CronJob last schedule time
  • [ ] Set appropriate startingDeadlineSeconds
  • [ ] Configure concurrencyPolicy
  • [ ] Set job history limits
  • [ ] Use correct timezone
  • [ ] Test CronJob schedules in staging
  • [Fix Kubernetes CronJob Failed](/articles/fix-kubernetes-cronjob-failed)
  • [Fix Kubernetes CronJob Not Scheduling](/articles/fix-kubernetes-cronjob-not-scheduling)
  • [Fix Kubernetes Job Not Completing](/articles/fix-kubernetes-job-not-completing)
  • [Fix Envoy Rate Limit Configuration with envoyproxy/ratelimit](envoyproxy-ratelimit-configuration-guide)
  • [Fix Fix Argocd App Not Syncing Issue in Kubernetes](fix-argocd-app-not-syncing)
  • [Fix Fix Argocd Sync Conflict Issue in Kubernetes](fix-argocd-sync-conflict)
  • [Fix ArgoCD Sync Timeout](fix-argocd-sync-timeout)
  • [How to Fix Cilium Identity Exhaustion and Endpoint Allocation Failed](fix-cilium-identity-exhaustion)

<script type="application/ld+json"> { "@context": "https://schema.org", "@type": "TechArticle", "headline": "Fix Kubernetes CronJob Missed Schedule", "description": "Troubleshoot Kubernetes CronJob missed schedule. Check timezone, concurrency policy, schedule format.", "url": "https://www.fixwikihub.com/fix-kubernetes-cronjob-missed-schedule", "publisher": { "@type": "Organization", "name": "FixWikiHub", "url": "https://www.fixwikihub.com" }, "author": { "@type": "Person", "name": "FixWikiHub Editorial Team" }, "datePublished": "2026-04-04T23:04:52.263Z", "dateModified": "2026-04-04T23:04:52.263Z" } </script>