{
  "schemaVersion": "1.0",
  "item": {
    "slug": "cron-scheduling",
    "name": "Cron & Scheduling",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/gitgoodordietrying/cron-scheduling",
    "canonicalUrl": "https://clawhub.ai/gitgoodordietrying/cron-scheduling",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/cron-scheduling",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=cron-scheduling",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "SKILL.md"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "slug": "cron-scheduling",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-05-02T05:43:55.815Z",
      "expiresAt": "2026-05-09T05:43:55.815Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=cron-scheduling",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=cron-scheduling",
        "contentDisposition": "attachment; filename=\"cron-scheduling-1.0.0.zip\"",
        "redirectLocation": null,
        "bodySnippet": null,
        "slug": "cron-scheduling"
      },
      "scope": "item",
      "summary": "Item download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this item.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/cron-scheduling"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/cron-scheduling",
    "agentPageUrl": "https://openagent3.xyz/skills/cron-scheduling/agent",
    "manifestUrl": "https://openagent3.xyz/skills/cron-scheduling/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/cron-scheduling/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "Cron & Scheduling",
        "body": "Schedule and manage recurring tasks. Covers cron syntax, crontab management, systemd timers, one-off scheduling, timezone handling, monitoring, and common failure patterns."
      },
      {
        "title": "When to Use",
        "body": "Running scripts on a schedule (backups, reports, cleanup)\nSetting up systemd timers (modern cron alternative)\nDebugging why a scheduled job didn't run\nHandling timezones in scheduled tasks\nMonitoring and alerting on job failures\nRunning one-off delayed commands"
      },
      {
        "title": "The five fields",
        "body": "┌───────── minute (0-59)\n│ ┌─────── hour (0-23)\n│ │ ┌───── day of month (1-31)\n│ │ │ ┌─── month (1-12 or JAN-DEC)\n│ │ │ │ ┌─ day of week (0-7, 0 and 7 = Sunday, or SUN-SAT)\n│ │ │ │ │\n* * * * * command"
      },
      {
        "title": "Common schedules",
        "body": "# Every minute\n* * * * * /path/to/script.sh\n\n# Every 5 minutes\n*/5 * * * * /path/to/script.sh\n\n# Every hour at :00\n0 * * * * /path/to/script.sh\n\n# Every day at 2:30 AM\n30 2 * * * /path/to/script.sh\n\n# Every Monday at 9:00 AM\n0 9 * * 1 /path/to/script.sh\n\n# Every weekday at 8:00 AM\n0 8 * * 1-5 /path/to/script.sh\n\n# First day of every month at midnight\n0 0 1 * * /path/to/script.sh\n\n# Every 15 minutes during business hours (Mon-Fri 9-17)\n*/15 9-17 * * 1-5 /path/to/script.sh\n\n# Twice a day (9 AM and 5 PM)\n0 9,17 * * * /path/to/script.sh\n\n# Every quarter (Jan, Apr, Jul, Oct) on the 1st at midnight\n0 0 1 1,4,7,10 * /path/to/script.sh\n\n# Every Sunday at 3 AM\n0 3 * * 0 /path/to/script.sh"
      },
      {
        "title": "Special strings (shorthand)",
        "body": "@reboot    /path/to/script.sh   # Run once at startup\n@yearly    /path/to/script.sh   # 0 0 1 1 *\n@monthly   /path/to/script.sh   # 0 0 1 * *\n@weekly    /path/to/script.sh   # 0 0 * * 0\n@daily     /path/to/script.sh   # 0 0 * * *\n@hourly    /path/to/script.sh   # 0 * * * *"
      },
      {
        "title": "Crontab Management",
        "body": "# Edit current user's crontab\ncrontab -e\n\n# List current crontab\ncrontab -l\n\n# Edit another user's crontab (root)\nsudo crontab -u www-data -e\n\n# Remove all cron jobs (be careful!)\ncrontab -r\n\n# Install crontab from file\ncrontab mycrontab.txt\n\n# Backup crontab\ncrontab -l > crontab-backup-$(date +%Y%m%d).txt"
      },
      {
        "title": "Crontab best practices",
        "body": "# Set PATH explicitly (cron has minimal PATH)\nPATH=/usr/local/bin:/usr/bin:/bin\n\n# Set MAILTO for error notifications\nMAILTO=admin@example.com\n\n# Set shell explicitly\nSHELL=/bin/bash\n\n# Full crontab example\nPATH=/usr/local/bin:/usr/bin:/bin\nMAILTO=admin@example.com\nSHELL=/bin/bash\n\n# Backups\n0 2 * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1\n\n# Cleanup old logs\n0 3 * * 0 find /var/log/myapp -name \"*.log\" -mtime +30 -delete\n\n# Health check\n*/5 * * * * /opt/scripts/healthcheck.sh || /opt/scripts/alert.sh \"Health check failed\""
      },
      {
        "title": "Create a timer (modern cron replacement)",
        "body": "# /etc/systemd/system/backup.service\n[Unit]\nDescription=Daily backup\n\n[Service]\nType=oneshot\nExecStart=/opt/scripts/backup.sh\nUser=backup\nStandardOutput=journal\nStandardError=journal\n\n# /etc/systemd/system/backup.timer\n[Unit]\nDescription=Run backup daily at 2 AM\n\n[Timer]\nOnCalendar=*-*-* 02:00:00\nPersistent=true\nRandomizedDelaySec=300\n\n[Install]\nWantedBy=timers.target\n\n# Enable and start the timer\nsudo systemctl daemon-reload\nsudo systemctl enable --now backup.timer\n\n# Check timer status\nsystemctl list-timers\nsystemctl list-timers --all\n\n# Check last run\nsystemctl status backup.service\njournalctl -u backup.service --since today\n\n# Run manually (for testing)\nsudo systemctl start backup.service\n\n# Disable timer\nsudo systemctl disable --now backup.timer"
      },
      {
        "title": "OnCalendar syntax",
        "body": "# Systemd calendar expressions\n\n# Daily at midnight\nOnCalendar=daily\n# or: OnCalendar=*-*-* 00:00:00\n\n# Every Monday at 9 AM\nOnCalendar=Mon *-*-* 09:00:00\n\n# Every 15 minutes\nOnCalendar=*:0/15\n\n# Weekdays at 8 AM\nOnCalendar=Mon..Fri *-*-* 08:00:00\n\n# First of every month\nOnCalendar=*-*-01 00:00:00\n\n# Every 6 hours\nOnCalendar=0/6:00:00\n\n# Specific dates\nOnCalendar=2026-02-03 12:00:00\n\n# Test calendar expressions\nsystemd-analyze calendar \"Mon *-*-* 09:00:00\"\nsystemd-analyze calendar \"*:0/15\"\nsystemd-analyze calendar --iterations=5 \"Mon..Fri *-*-* 08:00:00\""
      },
      {
        "title": "Advantages over cron",
        "body": "Systemd timers vs cron:\n+ Logs in journald (journalctl -u service-name)\n+ Persistent: catches up on missed runs after reboot\n+ RandomizedDelaySec: prevents thundering herd\n+ Dependencies: can depend on network, mounts, etc.\n+ Resource limits: CPUQuota, MemoryMax, etc.\n+ No lost-email problem (MAILTO often misconfigured)\n- More files to create (service + timer)\n- More verbose configuration"
      },
      {
        "title": "at (run once at a specific time)",
        "body": "# Schedule a command\necho \"/opt/scripts/deploy.sh\" | at 2:00 AM tomorrow\necho \"reboot\" | at now + 30 minutes\necho \"/opt/scripts/report.sh\" | at 5:00 PM Friday\n\n# Interactive (type commands, Ctrl+D to finish)\nat 10:00 AM\n> /opt/scripts/task.sh\n> echo \"Done\" | mail -s \"Task complete\" admin@example.com\n> <Ctrl+D>\n\n# List pending jobs\natq\n\n# View job details\nat -c <job-number>\n\n# Remove a job\natrm <job-number>"
      },
      {
        "title": "sleep-based (simplest)",
        "body": "# Run something after a delay\n(sleep 3600 && /opt/scripts/task.sh) &\n\n# With nohup (survives logout)\nnohup bash -c \"sleep 7200 && /opt/scripts/task.sh\" &"
      },
      {
        "title": "Timezone Handling",
        "body": "# Cron runs in the system timezone by default\n# Check system timezone\ntimedatectl\ndate +%Z\n\n# Set timezone for a specific cron job\n# Method 1: TZ variable in crontab\nTZ=America/New_York\n0 9 * * * /opt/scripts/report.sh\n\n# Method 2: In the script itself\n#!/bin/bash\nexport TZ=UTC\n# All date operations now use UTC\n\n# Method 3: Wrapper\nTZ=Europe/London date '+%Y-%m-%d %H:%M:%S'\n\n# List available timezones\ntimedatectl list-timezones\ntimedatectl list-timezones | grep America"
      },
      {
        "title": "DST pitfalls",
        "body": "Problem: A job scheduled for 2:30 AM may run twice or not at all\nduring DST transitions.\n\n\"Spring forward\": 2:30 AM doesn't exist (clock jumps 2:00 → 3:00)\n\"Fall back\": 2:30 AM happens twice\n\nMitigation:\n1. Schedule critical jobs outside 1:00-3:00 AM\n2. Use UTC for the schedule: TZ=UTC in crontab\n3. Make jobs idempotent (safe to run twice)\n4. Systemd timers handle DST correctly"
      },
      {
        "title": "Why didn't my cron job run?",
        "body": "# 1. Check cron daemon is running\nsystemctl status cron    # Debian/Ubuntu\nsystemctl status crond   # CentOS/RHEL\n\n# 2. Check cron logs\ngrep CRON /var/log/syslog           # Debian/Ubuntu\ngrep CRON /var/log/cron             # CentOS/RHEL\njournalctl -u cron --since today    # systemd\n\n# 3. Check crontab actually exists\ncrontab -l\n\n# 4. Test the command manually (with cron's environment)\nenv -i HOME=$HOME SHELL=/bin/sh PATH=/usr/bin:/bin /opt/scripts/backup.sh\n# If it fails here but works normally → PATH or env issue\n\n# 5. Check permissions\nls -la /opt/scripts/backup.sh   # Must be executable\nls -la /var/spool/cron/         # Crontab file permissions\n\n# 6. Check for syntax errors in crontab\n# cron silently ignores lines with errors\n\n# 7. Check if output is being discarded\n# By default, cron emails output. If no MTA, output is lost.\n# Always redirect: >> /var/log/myjob.log 2>&1"
      },
      {
        "title": "Job wrapper with logging and alerting",
        "body": "#!/bin/bash\n# cron-wrapper.sh — Run a command with logging, timing, and error alerting\n# Usage: cron-wrapper.sh <job-name> <command> [args...]\n\nset -euo pipefail\n\nJOB_NAME=\"${1:?Usage: cron-wrapper.sh <job-name> <command> [args...]}\"\nshift\nCOMMAND=(\"$@\")\n\nLOG_DIR=\"/var/log/cron-jobs\"\nmkdir -p \"$LOG_DIR\"\nLOG_FILE=\"$LOG_DIR/$JOB_NAME.log\"\n\nlog() { echo \"[$(date -u '+%Y-%m-%dT%H:%M:%SZ')] $*\" >> \"$LOG_FILE\"; }\n\nlog \"START: ${COMMAND[*]}\"\nSTART_TIME=$(date +%s)\n\nif \"${COMMAND[@]}\" >> \"$LOG_FILE\" 2>&1; then\n    ELAPSED=$(( $(date +%s) - START_TIME ))\n    log \"SUCCESS (${ELAPSED}s)\"\nelse\n    EXIT_CODE=$?\n    ELAPSED=$(( $(date +%s) - START_TIME ))\n    log \"FAILED with exit code $EXIT_CODE (${ELAPSED}s)\"\n    # Alert (customize as needed)\n    echo \"Cron job '$JOB_NAME' failed with exit $EXIT_CODE\" | \\\n        mail -s \"CRON FAIL: $JOB_NAME\" admin@example.com 2>/dev/null || true\n    exit $EXIT_CODE\nfi\n\n# Use in crontab:\n0 2 * * * /opt/scripts/cron-wrapper.sh daily-backup /opt/scripts/backup.sh\n*/5 * * * * /opt/scripts/cron-wrapper.sh health-check /opt/scripts/healthcheck.sh"
      },
      {
        "title": "Lock to prevent overlap",
        "body": "# Prevent concurrent runs (job takes longer than interval)\n# Method 1: flock\n* * * * * flock -n /tmp/myjob.lock /opt/scripts/slow-job.sh\n\n# Method 2: In the script\nLOCKFILE=\"/tmp/myjob.lock\"\nexec 200>\"$LOCKFILE\"\nflock -n 200 || { echo \"Already running\"; exit 0; }\n# ... do work ..."
      },
      {
        "title": "Idempotent Job Patterns",
        "body": "# Idempotent backup (only creates if newer than last backup)\n#!/bin/bash\nBACKUP_DIR=\"/backups/$(date +%Y%m%d)\"\n[[ -d \"$BACKUP_DIR\" ]] && { echo \"Backup already exists\"; exit 0; }\nmkdir -p \"$BACKUP_DIR\"\npg_dump mydb > \"$BACKUP_DIR/mydb.sql\"\n\n# Idempotent cleanup (safe to run multiple times)\nfind /tmp/uploads -mtime +7 -type f -delete 2>/dev/null || true\n\n# Idempotent sync (rsync only transfers changes)\nrsync -az /data/ backup-server:/backups/data/"
      },
      {
        "title": "Tips",
        "body": "Always redirect output in cron jobs: >> /var/log/job.log 2>&1. Without this, output goes to mail (if configured) or is silently lost.\nTest cron jobs by running them with env -i to simulate cron's minimal environment. Most failures are caused by missing PATH or environment variables.\nUse flock to prevent overlapping runs when a job might take longer than its schedule interval.\nMake all scheduled jobs idempotent. If a job runs twice (DST, manual trigger, crash recovery), it should produce the same result.\nsystemd-analyze calendar is invaluable for verifying timer schedules before deploying.\nNever schedule critical jobs between 1:00 AM and 3:00 AM if DST applies. Use UTC schedules instead.\nLog the start time, end time, and exit code of every cron job. Without this, debugging failures after the fact is guesswork.\nPrefer systemd timers over cron for production services: you get journald logging, missed-run catchup (Persistent=true), and resource limits for free."
      }
    ],
    "body": "Cron & Scheduling\n\nSchedule and manage recurring tasks. Covers cron syntax, crontab management, systemd timers, one-off scheduling, timezone handling, monitoring, and common failure patterns.\n\nWhen to Use\nRunning scripts on a schedule (backups, reports, cleanup)\nSetting up systemd timers (modern cron alternative)\nDebugging why a scheduled job didn't run\nHandling timezones in scheduled tasks\nMonitoring and alerting on job failures\nRunning one-off delayed commands\nCron Syntax\nThe five fields\n┌───────── minute (0-59)\n│ ┌─────── hour (0-23)\n│ │ ┌───── day of month (1-31)\n│ │ │ ┌─── month (1-12 or JAN-DEC)\n│ │ │ │ ┌─ day of week (0-7, 0 and 7 = Sunday, or SUN-SAT)\n│ │ │ │ │\n* * * * * command\n\nCommon schedules\n# Every minute\n* * * * * /path/to/script.sh\n\n# Every 5 minutes\n*/5 * * * * /path/to/script.sh\n\n# Every hour at :00\n0 * * * * /path/to/script.sh\n\n# Every day at 2:30 AM\n30 2 * * * /path/to/script.sh\n\n# Every Monday at 9:00 AM\n0 9 * * 1 /path/to/script.sh\n\n# Every weekday at 8:00 AM\n0 8 * * 1-5 /path/to/script.sh\n\n# First day of every month at midnight\n0 0 1 * * /path/to/script.sh\n\n# Every 15 minutes during business hours (Mon-Fri 9-17)\n*/15 9-17 * * 1-5 /path/to/script.sh\n\n# Twice a day (9 AM and 5 PM)\n0 9,17 * * * /path/to/script.sh\n\n# Every quarter (Jan, Apr, Jul, Oct) on the 1st at midnight\n0 0 1 1,4,7,10 * /path/to/script.sh\n\n# Every Sunday at 3 AM\n0 3 * * 0 /path/to/script.sh\n\nSpecial strings (shorthand)\n@reboot    /path/to/script.sh   # Run once at startup\n@yearly    /path/to/script.sh   # 0 0 1 1 *\n@monthly   /path/to/script.sh   # 0 0 1 * *\n@weekly    /path/to/script.sh   # 0 0 * * 0\n@daily     /path/to/script.sh   # 0 0 * * *\n@hourly    /path/to/script.sh   # 0 * * * *\n\nCrontab Management\n# Edit current user's crontab\ncrontab -e\n\n# List current crontab\ncrontab -l\n\n# Edit another user's crontab (root)\nsudo crontab -u www-data -e\n\n# Remove all cron jobs (be careful!)\ncrontab -r\n\n# Install crontab from file\ncrontab mycrontab.txt\n\n# Backup crontab\ncrontab -l > crontab-backup-$(date +%Y%m%d).txt\n\nCrontab best practices\n# Set PATH explicitly (cron has minimal PATH)\nPATH=/usr/local/bin:/usr/bin:/bin\n\n# Set MAILTO for error notifications\nMAILTO=admin@example.com\n\n# Set shell explicitly\nSHELL=/bin/bash\n\n# Full crontab example\nPATH=/usr/local/bin:/usr/bin:/bin\nMAILTO=admin@example.com\nSHELL=/bin/bash\n\n# Backups\n0 2 * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1\n\n# Cleanup old logs\n0 3 * * 0 find /var/log/myapp -name \"*.log\" -mtime +30 -delete\n\n# Health check\n*/5 * * * * /opt/scripts/healthcheck.sh || /opt/scripts/alert.sh \"Health check failed\"\n\nSystemd Timers\nCreate a timer (modern cron replacement)\n# /etc/systemd/system/backup.service\n[Unit]\nDescription=Daily backup\n\n[Service]\nType=oneshot\nExecStart=/opt/scripts/backup.sh\nUser=backup\nStandardOutput=journal\nStandardError=journal\n\n# /etc/systemd/system/backup.timer\n[Unit]\nDescription=Run backup daily at 2 AM\n\n[Timer]\nOnCalendar=*-*-* 02:00:00\nPersistent=true\nRandomizedDelaySec=300\n\n[Install]\nWantedBy=timers.target\n\n# Enable and start the timer\nsudo systemctl daemon-reload\nsudo systemctl enable --now backup.timer\n\n# Check timer status\nsystemctl list-timers\nsystemctl list-timers --all\n\n# Check last run\nsystemctl status backup.service\njournalctl -u backup.service --since today\n\n# Run manually (for testing)\nsudo systemctl start backup.service\n\n# Disable timer\nsudo systemctl disable --now backup.timer\n\nOnCalendar syntax\n# Systemd calendar expressions\n\n# Daily at midnight\nOnCalendar=daily\n# or: OnCalendar=*-*-* 00:00:00\n\n# Every Monday at 9 AM\nOnCalendar=Mon *-*-* 09:00:00\n\n# Every 15 minutes\nOnCalendar=*:0/15\n\n# Weekdays at 8 AM\nOnCalendar=Mon..Fri *-*-* 08:00:00\n\n# First of every month\nOnCalendar=*-*-01 00:00:00\n\n# Every 6 hours\nOnCalendar=0/6:00:00\n\n# Specific dates\nOnCalendar=2026-02-03 12:00:00\n\n# Test calendar expressions\nsystemd-analyze calendar \"Mon *-*-* 09:00:00\"\nsystemd-analyze calendar \"*:0/15\"\nsystemd-analyze calendar --iterations=5 \"Mon..Fri *-*-* 08:00:00\"\n\nAdvantages over cron\nSystemd timers vs cron:\n+ Logs in journald (journalctl -u service-name)\n+ Persistent: catches up on missed runs after reboot\n+ RandomizedDelaySec: prevents thundering herd\n+ Dependencies: can depend on network, mounts, etc.\n+ Resource limits: CPUQuota, MemoryMax, etc.\n+ No lost-email problem (MAILTO often misconfigured)\n- More files to create (service + timer)\n- More verbose configuration\n\nOne-Off Scheduling\nat (run once at a specific time)\n# Schedule a command\necho \"/opt/scripts/deploy.sh\" | at 2:00 AM tomorrow\necho \"reboot\" | at now + 30 minutes\necho \"/opt/scripts/report.sh\" | at 5:00 PM Friday\n\n# Interactive (type commands, Ctrl+D to finish)\nat 10:00 AM\n> /opt/scripts/task.sh\n> echo \"Done\" | mail -s \"Task complete\" admin@example.com\n> <Ctrl+D>\n\n# List pending jobs\natq\n\n# View job details\nat -c <job-number>\n\n# Remove a job\natrm <job-number>\n\nsleep-based (simplest)\n# Run something after a delay\n(sleep 3600 && /opt/scripts/task.sh) &\n\n# With nohup (survives logout)\nnohup bash -c \"sleep 7200 && /opt/scripts/task.sh\" &\n\nTimezone Handling\n# Cron runs in the system timezone by default\n# Check system timezone\ntimedatectl\ndate +%Z\n\n# Set timezone for a specific cron job\n# Method 1: TZ variable in crontab\nTZ=America/New_York\n0 9 * * * /opt/scripts/report.sh\n\n# Method 2: In the script itself\n#!/bin/bash\nexport TZ=UTC\n# All date operations now use UTC\n\n# Method 3: Wrapper\nTZ=Europe/London date '+%Y-%m-%d %H:%M:%S'\n\n# List available timezones\ntimedatectl list-timezones\ntimedatectl list-timezones | grep America\n\nDST pitfalls\nProblem: A job scheduled for 2:30 AM may run twice or not at all\nduring DST transitions.\n\n\"Spring forward\": 2:30 AM doesn't exist (clock jumps 2:00 → 3:00)\n\"Fall back\": 2:30 AM happens twice\n\nMitigation:\n1. Schedule critical jobs outside 1:00-3:00 AM\n2. Use UTC for the schedule: TZ=UTC in crontab\n3. Make jobs idempotent (safe to run twice)\n4. Systemd timers handle DST correctly\n\nMonitoring and Debugging\nWhy didn't my cron job run?\n# 1. Check cron daemon is running\nsystemctl status cron    # Debian/Ubuntu\nsystemctl status crond   # CentOS/RHEL\n\n# 2. Check cron logs\ngrep CRON /var/log/syslog           # Debian/Ubuntu\ngrep CRON /var/log/cron             # CentOS/RHEL\njournalctl -u cron --since today    # systemd\n\n# 3. Check crontab actually exists\ncrontab -l\n\n# 4. Test the command manually (with cron's environment)\nenv -i HOME=$HOME SHELL=/bin/sh PATH=/usr/bin:/bin /opt/scripts/backup.sh\n# If it fails here but works normally → PATH or env issue\n\n# 5. Check permissions\nls -la /opt/scripts/backup.sh   # Must be executable\nls -la /var/spool/cron/         # Crontab file permissions\n\n# 6. Check for syntax errors in crontab\n# cron silently ignores lines with errors\n\n# 7. Check if output is being discarded\n# By default, cron emails output. If no MTA, output is lost.\n# Always redirect: >> /var/log/myjob.log 2>&1\n\nJob wrapper with logging and alerting\n#!/bin/bash\n# cron-wrapper.sh — Run a command with logging, timing, and error alerting\n# Usage: cron-wrapper.sh <job-name> <command> [args...]\n\nset -euo pipefail\n\nJOB_NAME=\"${1:?Usage: cron-wrapper.sh <job-name> <command> [args...]}\"\nshift\nCOMMAND=(\"$@\")\n\nLOG_DIR=\"/var/log/cron-jobs\"\nmkdir -p \"$LOG_DIR\"\nLOG_FILE=\"$LOG_DIR/$JOB_NAME.log\"\n\nlog() { echo \"[$(date -u '+%Y-%m-%dT%H:%M:%SZ')] $*\" >> \"$LOG_FILE\"; }\n\nlog \"START: ${COMMAND[*]}\"\nSTART_TIME=$(date +%s)\n\nif \"${COMMAND[@]}\" >> \"$LOG_FILE\" 2>&1; then\n    ELAPSED=$(( $(date +%s) - START_TIME ))\n    log \"SUCCESS (${ELAPSED}s)\"\nelse\n    EXIT_CODE=$?\n    ELAPSED=$(( $(date +%s) - START_TIME ))\n    log \"FAILED with exit code $EXIT_CODE (${ELAPSED}s)\"\n    # Alert (customize as needed)\n    echo \"Cron job '$JOB_NAME' failed with exit $EXIT_CODE\" | \\\n        mail -s \"CRON FAIL: $JOB_NAME\" admin@example.com 2>/dev/null || true\n    exit $EXIT_CODE\nfi\n\n# Use in crontab:\n0 2 * * * /opt/scripts/cron-wrapper.sh daily-backup /opt/scripts/backup.sh\n*/5 * * * * /opt/scripts/cron-wrapper.sh health-check /opt/scripts/healthcheck.sh\n\nLock to prevent overlap\n# Prevent concurrent runs (job takes longer than interval)\n# Method 1: flock\n* * * * * flock -n /tmp/myjob.lock /opt/scripts/slow-job.sh\n\n# Method 2: In the script\nLOCKFILE=\"/tmp/myjob.lock\"\nexec 200>\"$LOCKFILE\"\nflock -n 200 || { echo \"Already running\"; exit 0; }\n# ... do work ...\n\nIdempotent Job Patterns\n# Idempotent backup (only creates if newer than last backup)\n#!/bin/bash\nBACKUP_DIR=\"/backups/$(date +%Y%m%d)\"\n[[ -d \"$BACKUP_DIR\" ]] && { echo \"Backup already exists\"; exit 0; }\nmkdir -p \"$BACKUP_DIR\"\npg_dump mydb > \"$BACKUP_DIR/mydb.sql\"\n\n# Idempotent cleanup (safe to run multiple times)\nfind /tmp/uploads -mtime +7 -type f -delete 2>/dev/null || true\n\n# Idempotent sync (rsync only transfers changes)\nrsync -az /data/ backup-server:/backups/data/\n\nTips\nAlways redirect output in cron jobs: >> /var/log/job.log 2>&1. Without this, output goes to mail (if configured) or is silently lost.\nTest cron jobs by running them with env -i to simulate cron's minimal environment. Most failures are caused by missing PATH or environment variables.\nUse flock to prevent overlapping runs when a job might take longer than its schedule interval.\nMake all scheduled jobs idempotent. If a job runs twice (DST, manual trigger, crash recovery), it should produce the same result.\nsystemd-analyze calendar is invaluable for verifying timer schedules before deploying.\nNever schedule critical jobs between 1:00 AM and 3:00 AM if DST applies. Use UTC schedules instead.\nLog the start time, end time, and exit code of every cron job. Without this, debugging failures after the fact is guesswork.\nPrefer systemd timers over cron for production services: you get journald logging, missed-run catchup (Persistent=true), and resource limits for free."
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/gitgoodordietrying/cron-scheduling",
    "publisherUrl": "https://clawhub.ai/gitgoodordietrying/cron-scheduling",
    "owner": "gitgoodordietrying",
    "version": "1.0.0",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/cron-scheduling",
    "downloadUrl": "https://openagent3.xyz/downloads/cron-scheduling",
    "agentUrl": "https://openagent3.xyz/skills/cron-scheduling/agent",
    "manifestUrl": "https://openagent3.xyz/skills/cron-scheduling/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/cron-scheduling/agent.md"
  }
}