[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6FP3FDZBYK19HXKA59P02XM":3,"skill-same:01M6FP3FDZBYK19HXKA59P02XM":80},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":38,"linkOnly":39,"markdownContent":40,"markdownBody":41,"frontMatter":42,"refName":53,"commitSha":54,"skillDirectory":55,"forksCount":56,"repositoryHtmlUrl":57,"repositoryHomepageUrl":6,"ownerHtmlUrl":58,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":59,"install":60,"resources":73},"01M6FP3FDZBYK19HXKA59P02XM","cron-scheduling",null,"Schedule and manage recurring tasks with cron and systemd timers. Use when setting up cron jobs, writing systemd timer units, handling timezone-aware scheduling, monitoring failed jobs, implementing retry patterns, or debugging why a scheduled task didn't run.","kbarbel640-del\u002Fskills","kbarbel640-del","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F255315490?v=4","01M6CJPPQGCCESXMG3K7BBGQ5J","01M6CJPPX2PB2BCZERFVM2C2B3",1,"MIT","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fgitgoodordietrying\u002Fcron-scheduling\u002FSKILL.md",[],[18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"cron scheduling","cron","scheduling","use","retry","with","units","task","manage","timer","up","writing","recurring","schedule","cron jobs","monitoring","run","tasks","debugging","linux","2026-11-10 10:12:30",false,"---\nname: cron-scheduling\ndescription: Schedule and manage recurring tasks with cron and systemd timers. Use when setting up cron jobs, writing systemd timer units, handling timezone-aware scheduling, monitoring failed jobs, implementing retry patterns, or debugging why a scheduled task didn't run.\nmetadata: {\"clawdbot\":{\"emoji\":\"⏰\",\"requires\":{\"anyBins\":[\"crontab\",\"systemctl\",\"at\"]},\"os\":[\"linux\",\"darwin\"]}}\n---\n\n# 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\n## When to Use\n\n- Running scripts on a schedule (backups, reports, cleanup)\n- Setting up systemd timers (modern cron alternative)\n- Debugging why a scheduled job didn't run\n- Handling timezones in scheduled tasks\n- Monitoring and alerting on job failures\n- Running one-off delayed commands\n\n## Cron Syntax\n\n### The five fields\n\n```\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```\n\n### Common schedules\n\n```bash\n# Every minute\n* * * * * \u002Fpath\u002Fto\u002Fscript.sh\n\n# Every 5 minutes\n*\u002F5 * * * * \u002Fpath\u002Fto\u002Fscript.sh\n\n# Every hour at :00\n0 * * * * \u002Fpath\u002Fto\u002Fscript.sh\n\n# Every day at 2:30 AM\n30 2 * * * \u002Fpath\u002Fto\u002Fscript.sh\n\n# Every Monday at 9:00 AM\n0 9 * * 1 \u002Fpath\u002Fto\u002Fscript.sh\n\n# Every weekday at 8:00 AM\n0 8 * * 1-5 \u002Fpath\u002Fto\u002Fscript.sh\n\n# First day of every month at midnight\n0 0 1 * * \u002Fpath\u002Fto\u002Fscript.sh\n\n# Every 15 minutes during business hours (Mon-Fri 9-17)\n*\u002F15 9-17 * * 1-5 \u002Fpath\u002Fto\u002Fscript.sh\n\n# Twice a day (9 AM and 5 PM)\n0 9,17 * * * \u002Fpath\u002Fto\u002Fscript.sh\n\n# Every quarter (Jan, Apr, Jul, Oct) on the 1st at midnight\n0 0 1 1,4,7,10 * \u002Fpath\u002Fto\u002Fscript.sh\n\n# Every Sunday at 3 AM\n0 3 * * 0 \u002Fpath\u002Fto\u002Fscript.sh\n```\n\n### Special strings (shorthand)\n\n```bash\n@reboot    \u002Fpath\u002Fto\u002Fscript.sh   # Run once at startup\n@yearly    \u002Fpath\u002Fto\u002Fscript.sh   # 0 0 1 1 *\n@monthly   \u002Fpath\u002Fto\u002Fscript.sh   # 0 0 1 * *\n@weekly    \u002Fpath\u002Fto\u002Fscript.sh   # 0 0 * * 0\n@daily     \u002Fpath\u002Fto\u002Fscript.sh   # 0 0 * * *\n@hourly    \u002Fpath\u002Fto\u002Fscript.sh   # 0 * * * *\n```\n\n## Crontab Management\n\n```bash\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```\n\n### Crontab best practices\n\n```bash\n# Set PATH explicitly (cron has minimal PATH)\nPATH=\u002Fusr\u002Flocal\u002Fbin:\u002Fusr\u002Fbin:\u002Fbin\n\n# Set MAILTO for error notifications\nMAILTO=admin@example.com\n\n# Set shell explicitly\nSHELL=\u002Fbin\u002Fbash\n\n# Full crontab example\nPATH=\u002Fusr\u002Flocal\u002Fbin:\u002Fusr\u002Fbin:\u002Fbin\nMAILTO=admin@example.com\nSHELL=\u002Fbin\u002Fbash\n\n# Backups\n0 2 * * * \u002Fopt\u002Fscripts\u002Fbackup.sh >> \u002Fvar\u002Flog\u002Fbackup.log 2>&1\n\n# Cleanup old logs\n0 3 * * 0 find \u002Fvar\u002Flog\u002Fmyapp -name \"*.log\" -mtime +30 -delete\n\n# Health check\n*\u002F5 * * * * \u002Fopt\u002Fscripts\u002Fhealthcheck.sh || \u002Fopt\u002Fscripts\u002Falert.sh \"Health check failed\"\n```\n\n## Systemd Timers\n\n### Create a timer (modern cron replacement)\n\n```ini\n# \u002Fetc\u002Fsystemd\u002Fsystem\u002Fbackup.service\n[Unit]\nDescription=Daily backup\n\n[Service]\nType=oneshot\nExecStart=\u002Fopt\u002Fscripts\u002Fbackup.sh\nUser=backup\nStandardOutput=journal\nStandardError=journal\n```\n\n```ini\n# \u002Fetc\u002Fsystemd\u002Fsystem\u002Fbackup.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\n```bash\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```\n\n### OnCalendar syntax\n\n```ini\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\u002F15\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\u002F6: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\u002F15\"\nsystemd-analyze calendar --iterations=5 \"Mon..Fri *-*-* 08:00:00\"\n```\n\n### Advantages over cron\n\n```\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```\n\n## One-Off Scheduling\n\n### at (run once at a specific time)\n\n```bash\n# Schedule a command\necho \"\u002Fopt\u002Fscripts\u002Fdeploy.sh\" | at 2:00 AM tomorrow\necho \"reboot\" | at now + 30 minutes\necho \"\u002Fopt\u002Fscripts\u002Freport.sh\" | at 5:00 PM Friday\n\n# Interactive (type commands, Ctrl+D to finish)\nat 10:00 AM\n> \u002Fopt\u002Fscripts\u002Ftask.sh\n> echo \"Done\" | mail -s \"Task complete\" admin@example.com\n> \u003CCtrl+D>\n\n# List pending jobs\natq\n\n# View job details\nat -c \u003Cjob-number>\n\n# Remove a job\natrm \u003Cjob-number>\n```\n\n### sleep-based (simplest)\n\n```bash\n# Run something after a delay\n(sleep 3600 && \u002Fopt\u002Fscripts\u002Ftask.sh) &\n\n# With nohup (survives logout)\nnohup bash -c \"sleep 7200 && \u002Fopt\u002Fscripts\u002Ftask.sh\" &\n```\n\n## Timezone Handling\n\n```bash\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\u002FNew_York\n0 9 * * * \u002Fopt\u002Fscripts\u002Freport.sh\n\n# Method 2: In the script itself\n#!\u002Fbin\u002Fbash\nexport TZ=UTC\n# All date operations now use UTC\n\n# Method 3: Wrapper\nTZ=Europe\u002FLondon date '+%Y-%m-%d %H:%M:%S'\n\n# List available timezones\ntimedatectl list-timezones\ntimedatectl list-timezones | grep America\n```\n\n### DST pitfalls\n\n```\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```\n\n## Monitoring and Debugging\n\n### Why didn't my cron job run?\n\n```bash\n# 1. Check cron daemon is running\nsystemctl status cron    # Debian\u002FUbuntu\nsystemctl status crond   # CentOS\u002FRHEL\n\n# 2. Check cron logs\ngrep CRON \u002Fvar\u002Flog\u002Fsyslog           # Debian\u002FUbuntu\ngrep CRON \u002Fvar\u002Flog\u002Fcron             # CentOS\u002FRHEL\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=\u002Fbin\u002Fsh PATH=\u002Fusr\u002Fbin:\u002Fbin \u002Fopt\u002Fscripts\u002Fbackup.sh\n# If it fails here but works normally → PATH or env issue\n\n# 5. Check permissions\nls -la \u002Fopt\u002Fscripts\u002Fbackup.sh   # Must be executable\nls -la \u002Fvar\u002Fspool\u002Fcron\u002F         # 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: >> \u002Fvar\u002Flog\u002Fmyjob.log 2>&1\n```\n\n### Job wrapper with logging and alerting\n\n```bash\n#!\u002Fbin\u002Fbash\n# cron-wrapper.sh — Run a command with logging, timing, and error alerting\n# Usage: cron-wrapper.sh \u003Cjob-name> \u003Ccommand> [args...]\n\nset -euo pipefail\n\nJOB_NAME=\"${1:?Usage: cron-wrapper.sh \u003Cjob-name> \u003Ccommand> [args...]}\"\nshift\nCOMMAND=(\"$@\")\n\nLOG_DIR=\"\u002Fvar\u002Flog\u002Fcron-jobs\"\nmkdir -p \"$LOG_DIR\"\nLOG_FILE=\"$LOG_DIR\u002F$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>\u002Fdev\u002Fnull || true\n    exit $EXIT_CODE\nfi\n```\n\n```bash\n# Use in crontab:\n0 2 * * * \u002Fopt\u002Fscripts\u002Fcron-wrapper.sh daily-backup \u002Fopt\u002Fscripts\u002Fbackup.sh\n*\u002F5 * * * * \u002Fopt\u002Fscripts\u002Fcron-wrapper.sh health-check \u002Fopt\u002Fscripts\u002Fhealthcheck.sh\n```\n\n### Lock to prevent overlap\n\n```bash\n# Prevent concurrent runs (job takes longer than interval)\n# Method 1: flock\n* * * * * flock -n \u002Ftmp\u002Fmyjob.lock \u002Fopt\u002Fscripts\u002Fslow-job.sh\n\n# Method 2: In the script\nLOCKFILE=\"\u002Ftmp\u002Fmyjob.lock\"\nexec 200>\"$LOCKFILE\"\nflock -n 200 || { echo \"Already running\"; exit 0; }\n# ... do work ...\n```\n\n## Idempotent Job Patterns\n\n```bash\n# Idempotent backup (only creates if newer than last backup)\n#!\u002Fbin\u002Fbash\nBACKUP_DIR=\"\u002Fbackups\u002F$(date +%Y%m%d)\"\n[[ -d \"$BACKUP_DIR\" ]] && { echo \"Backup already exists\"; exit 0; }\nmkdir -p \"$BACKUP_DIR\"\npg_dump mydb > \"$BACKUP_DIR\u002Fmydb.sql\"\n\n# Idempotent cleanup (safe to run multiple times)\nfind \u002Ftmp\u002Fuploads -mtime +7 -type f -delete 2>\u002Fdev\u002Fnull || true\n\n# Idempotent sync (rsync only transfers changes)\nrsync -az \u002Fdata\u002F backup-server:\u002Fbackups\u002Fdata\u002F\n```\n\n## Tips\n\n- Always redirect output in cron jobs: `>> \u002Fvar\u002Flog\u002Fjob.log 2>&1`. Without this, output goes to mail (if configured) or is silently lost.\n- Test cron jobs by running them with `env -i` to simulate cron's minimal environment. Most failures are caused by missing `PATH` or environment variables.\n- Use `flock` to prevent overlapping runs when a job might take longer than its schedule interval.\n- Make all scheduled jobs idempotent. If a job runs twice (DST, manual trigger, crash recovery), it should produce the same result.\n- `systemd-analyze calendar` is invaluable for verifying timer schedules before deploying.\n- Never schedule critical jobs between 1:00 AM and 3:00 AM if DST applies. Use UTC schedules instead.\n- Log the start time, end time, and exit code of every cron job. Without this, debugging failures after the fact is guesswork.\n- Prefer systemd timers over cron for production services: you get journald logging, missed-run catchup (`Persistent=true`), and resource limits for free.\n","# 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\n## When to Use\n\n- Running scripts on a schedule (backups, reports, cleanup)\n- Setting up systemd timers (modern cron alternative)\n- Debugging why a scheduled job didn't run\n- Handling timezones in scheduled tasks\n- Monitoring and alerting on job failures\n- Running one-off delayed commands\n\n## Cron Syntax\n\n### The five fields\n\n```\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```\n\n### Common schedules\n\n```bash\n# Every minute\n* * * * * \u002Fpath\u002Fto\u002Fscript.sh\n\n# Every 5 minutes\n*\u002F5 * * * * \u002Fpath\u002Fto\u002Fscript.sh\n\n# Every hour at :00\n0 * * * * \u002Fpath\u002Fto\u002Fscript.sh\n\n# Every day at 2:30 AM\n30 2 * * * \u002Fpath\u002Fto\u002Fscript.sh\n\n# Every Monday at 9:00 AM\n0 9 * * 1 \u002Fpath\u002Fto\u002Fscript.sh\n\n# Every weekday at 8:00 AM\n0 8 * * 1-5 \u002Fpath\u002Fto\u002Fscript.sh\n\n# First day of every month at midnight\n0 0 1 * * \u002Fpath\u002Fto\u002Fscript.sh\n\n# Every 15 minutes during business hours (Mon-Fri 9-17)\n*\u002F15 9-17 * * 1-5 \u002Fpath\u002Fto\u002Fscript.sh\n\n# Twice a day (9 AM and 5 PM)\n0 9,17 * * * \u002Fpath\u002Fto\u002Fscript.sh\n\n# Every quarter (Jan, Apr, Jul, Oct) on the 1st at midnight\n0 0 1 1,4,7,10 * \u002Fpath\u002Fto\u002Fscript.sh\n\n# Every Sunday at 3 AM\n0 3 * * 0 \u002Fpath\u002Fto\u002Fscript.sh\n```\n\n### Special strings (shorthand)\n\n```bash\n@reboot    \u002Fpath\u002Fto\u002Fscript.sh   # Run once at startup\n@yearly    \u002Fpath\u002Fto\u002Fscript.sh   # 0 0 1 1 *\n@monthly   \u002Fpath\u002Fto\u002Fscript.sh   # 0 0 1 * *\n@weekly    \u002Fpath\u002Fto\u002Fscript.sh   # 0 0 * * 0\n@daily     \u002Fpath\u002Fto\u002Fscript.sh   # 0 0 * * *\n@hourly    \u002Fpath\u002Fto\u002Fscript.sh   # 0 * * * *\n```\n\n## Crontab Management\n\n```bash\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```\n\n### Crontab best practices\n\n```bash\n# Set PATH explicitly (cron has minimal PATH)\nPATH=\u002Fusr\u002Flocal\u002Fbin:\u002Fusr\u002Fbin:\u002Fbin\n\n# Set MAILTO for error notifications\nMAILTO=admin@example.com\n\n# Set shell explicitly\nSHELL=\u002Fbin\u002Fbash\n\n# Full crontab example\nPATH=\u002Fusr\u002Flocal\u002Fbin:\u002Fusr\u002Fbin:\u002Fbin\nMAILTO=admin@example.com\nSHELL=\u002Fbin\u002Fbash\n\n# Backups\n0 2 * * * \u002Fopt\u002Fscripts\u002Fbackup.sh >> \u002Fvar\u002Flog\u002Fbackup.log 2>&1\n\n# Cleanup old logs\n0 3 * * 0 find \u002Fvar\u002Flog\u002Fmyapp -name \"*.log\" -mtime +30 -delete\n\n# Health check\n*\u002F5 * * * * \u002Fopt\u002Fscripts\u002Fhealthcheck.sh || \u002Fopt\u002Fscripts\u002Falert.sh \"Health check failed\"\n```\n\n## Systemd Timers\n\n### Create a timer (modern cron replacement)\n\n```ini\n# \u002Fetc\u002Fsystemd\u002Fsystem\u002Fbackup.service\n[Unit]\nDescription=Daily backup\n\n[Service]\nType=oneshot\nExecStart=\u002Fopt\u002Fscripts\u002Fbackup.sh\nUser=backup\nStandardOutput=journal\nStandardError=journal\n```\n\n```ini\n# \u002Fetc\u002Fsystemd\u002Fsystem\u002Fbackup.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\n```bash\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```\n\n### OnCalendar syntax\n\n```ini\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\u002F15\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\u002F6: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\u002F15\"\nsystemd-analyze calendar --iterations=5 \"Mon..Fri *-*-* 08:00:00\"\n```\n\n### Advantages over cron\n\n```\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```\n\n## One-Off Scheduling\n\n### at (run once at a specific time)\n\n```bash\n# Schedule a command\necho \"\u002Fopt\u002Fscripts\u002Fdeploy.sh\" | at 2:00 AM tomorrow\necho \"reboot\" | at now + 30 minutes\necho \"\u002Fopt\u002Fscripts\u002Freport.sh\" | at 5:00 PM Friday\n\n# Interactive (type commands, Ctrl+D to finish)\nat 10:00 AM\n> \u002Fopt\u002Fscripts\u002Ftask.sh\n> echo \"Done\" | mail -s \"Task complete\" admin@example.com\n> \u003CCtrl+D>\n\n# List pending jobs\natq\n\n# View job details\nat -c \u003Cjob-number>\n\n# Remove a job\natrm \u003Cjob-number>\n```\n\n### sleep-based (simplest)\n\n```bash\n# Run something after a delay\n(sleep 3600 && \u002Fopt\u002Fscripts\u002Ftask.sh) &\n\n# With nohup (survives logout)\nnohup bash -c \"sleep 7200 && \u002Fopt\u002Fscripts\u002Ftask.sh\" &\n```\n\n## Timezone Handling\n\n```bash\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\u002FNew_York\n0 9 * * * \u002Fopt\u002Fscripts\u002Freport.sh\n\n# Method 2: In the script itself\n#!\u002Fbin\u002Fbash\nexport TZ=UTC\n# All date operations now use UTC\n\n# Method 3: Wrapper\nTZ=Europe\u002FLondon date '+%Y-%m-%d %H:%M:%S'\n\n# List available timezones\ntimedatectl list-timezones\ntimedatectl list-timezones | grep America\n```\n\n### DST pitfalls\n\n```\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```\n\n## Monitoring and Debugging\n\n### Why didn't my cron job run?\n\n```bash\n# 1. Check cron daemon is running\nsystemctl status cron    # Debian\u002FUbuntu\nsystemctl status crond   # CentOS\u002FRHEL\n\n# 2. Check cron logs\ngrep CRON \u002Fvar\u002Flog\u002Fsyslog           # Debian\u002FUbuntu\ngrep CRON \u002Fvar\u002Flog\u002Fcron             # CentOS\u002FRHEL\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=\u002Fbin\u002Fsh PATH=\u002Fusr\u002Fbin:\u002Fbin \u002Fopt\u002Fscripts\u002Fbackup.sh\n# If it fails here but works normally → PATH or env issue\n\n# 5. Check permissions\nls -la \u002Fopt\u002Fscripts\u002Fbackup.sh   # Must be executable\nls -la \u002Fvar\u002Fspool\u002Fcron\u002F         # 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: >> \u002Fvar\u002Flog\u002Fmyjob.log 2>&1\n```\n\n### Job wrapper with logging and alerting\n\n```bash\n#!\u002Fbin\u002Fbash\n# cron-wrapper.sh — Run a command with logging, timing, and error alerting\n# Usage: cron-wrapper.sh \u003Cjob-name> \u003Ccommand> [args...]\n\nset -euo pipefail\n\nJOB_NAME=\"${1:?Usage: cron-wrapper.sh \u003Cjob-name> \u003Ccommand> [args...]}\"\nshift\nCOMMAND=(\"$@\")\n\nLOG_DIR=\"\u002Fvar\u002Flog\u002Fcron-jobs\"\nmkdir -p \"$LOG_DIR\"\nLOG_FILE=\"$LOG_DIR\u002F$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>\u002Fdev\u002Fnull || true\n    exit $EXIT_CODE\nfi\n```\n\n```bash\n# Use in crontab:\n0 2 * * * \u002Fopt\u002Fscripts\u002Fcron-wrapper.sh daily-backup \u002Fopt\u002Fscripts\u002Fbackup.sh\n*\u002F5 * * * * \u002Fopt\u002Fscripts\u002Fcron-wrapper.sh health-check \u002Fopt\u002Fscripts\u002Fhealthcheck.sh\n```\n\n### Lock to prevent overlap\n\n```bash\n# Prevent concurrent runs (job takes longer than interval)\n# Method 1: flock\n* * * * * flock -n \u002Ftmp\u002Fmyjob.lock \u002Fopt\u002Fscripts\u002Fslow-job.sh\n\n# Method 2: In the script\nLOCKFILE=\"\u002Ftmp\u002Fmyjob.lock\"\nexec 200>\"$LOCKFILE\"\nflock -n 200 || { echo \"Already running\"; exit 0; }\n# ... do work ...\n```\n\n## Idempotent Job Patterns\n\n```bash\n# Idempotent backup (only creates if newer than last backup)\n#!\u002Fbin\u002Fbash\nBACKUP_DIR=\"\u002Fbackups\u002F$(date +%Y%m%d)\"\n[[ -d \"$BACKUP_DIR\" ]] && { echo \"Backup already exists\"; exit 0; }\nmkdir -p \"$BACKUP_DIR\"\npg_dump mydb > \"$BACKUP_DIR\u002Fmydb.sql\"\n\n# Idempotent cleanup (safe to run multiple times)\nfind \u002Ftmp\u002Fuploads -mtime +7 -type f -delete 2>\u002Fdev\u002Fnull || true\n\n# Idempotent sync (rsync only transfers changes)\nrsync -az \u002Fdata\u002F backup-server:\u002Fbackups\u002Fdata\u002F\n```\n\n## Tips\n\n- Always redirect output in cron jobs: `>> \u002Fvar\u002Flog\u002Fjob.log 2>&1`. Without this, output goes to mail (if configured) or is silently lost.\n- Test cron jobs by running them with `env -i` to simulate cron's minimal environment. Most failures are caused by missing `PATH` or environment variables.\n- Use `flock` to prevent overlapping runs when a job might take longer than its schedule interval.\n- Make all scheduled jobs idempotent. If a job runs twice (DST, manual trigger, crash recovery), it should produce the same result.\n- `systemd-analyze calendar` is invaluable for verifying timer schedules before deploying.\n- Never schedule critical jobs between 1:00 AM and 3:00 AM if DST applies. Use UTC schedules instead.\n- Log the start time, end time, and exit code of every cron job. Without this, debugging failures after the fact is guesswork.\n- Prefer systemd timers over cron for production services: you get journald logging, missed-run catchup (`Persistent=true`), and resource limits for free.\n",{"name":5,"metadata":43,"description":7},{"clawdbot":44},{"os":45,"emoji":47,"requires":48},[37,46],"darwin","⏰",{"anyBins":49},[50,51,52],"crontab","systemctl","at","main","e7e336ac628c3e3bd771aff1aaa469c93778f94a","skills\u002Fgitgoodordietrying\u002Fcron-scheduling",0,"https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del","2026-08-23 22:39:37",{"source":8,"skillName":5,"commands":61,"prompt":71,"zipAvailable":72},[62,65,68],{"tool":63,"command":64},"npx","npx skills add kbarbel640-del\u002Fskills --skill cron-scheduling",{"tool":66,"command":67},"bunx","bunx skills add kbarbel640-del\u002Fskills --skill cron-scheduling",{"tool":69,"command":70},"pnpm","pnpm dlx skills add kbarbel640-del\u002Fskills --skill cron-scheduling","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fgitgoodordietrying\u002Fcron-scheduling\u002FSKILL.md",true,[74],{"path":75,"resourceType":76,"resourceTypeDesc":77,"sizeBytes":78,"sourceHtmlUrl":79},"_meta.json","REFERENCE","参考文档",296,"https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fgitgoodordietrying\u002Fcron-scheduling\u002F_meta.json",[81,111,139,160,166,185],{"skillNo":82,"name":83,"nameZh":6,"description":84,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":85,"categories":86,"keywords":92,"publishedAt":38},"01M6G8J55967D6281ACMKAKTEQ","crowd-prompting","A marketplace where AI agents improve prompts, system instructions, tool descriptions, and other text-based content with domain expertise from real-world operations — and earn tokens for valuable contributions.","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzzadrian\u002Fcrowd-prompting\u002FSKILL.md",[87],{"categoryNo":88,"categoryName":89,"taxonomyType":90,"taxonomyTypeDesc":91},"01JBSEEDCAT000000000000036","内容创作者","PROFESSION","适用职业",[93,94,95,23,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110],"crowd prompting","prompting","api","tokens","domain","world","prompts","operations","instructions","text","tool","system","improve","productivity","marketplace","https","content","ai agents",{"skillNo":112,"name":113,"nameZh":6,"description":114,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":115,"categories":116,"keywords":122,"publishedAt":38},"01M6G8J0PKPAYVET6BWE8W4C26","dilemm-ai","Compete in DilemmAI, the prisoner's dilemma AI arena at dilemm.ai. Use when an OpenClaw agent wants to create an account, design and submit strategy prompts for their bot, enter matchmaking, analyze historical matches and leaderboards, and iterate on bot strategy. Requires headless browser automation (no API available yet).","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzyori\u002Fdilemm-ai\u002FSKILL.md",[117],{"categoryNo":118,"categoryName":119,"taxonomyType":120,"taxonomyTypeDesc":121},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[123,95,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,99,21],"dilemm ai","arena","analyze","browser","openclaw agent","create","automation","browser automation","bot","openclaw","design","strategy","headless","compete","submit","matchmaking",{"skillNo":140,"name":141,"nameZh":6,"description":142,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":143,"categories":144,"keywords":148,"publishedAt":38},"01M6G8HWAF3K9SW7NP3Z83CPJ6","arxiv-paper-reviews","Interact with arXiv Crawler API to fetch papers, read reviews, and submit comments. Use when working with arXiv papers, fetching paper lists by date\u002Fcategory\u002Finterest, viewing paper details with comments, or submitting paper reviews via API at http:\u002F\u002F150.158.152.82:8000.","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzxrys\u002Fweak-accept\u002FSKILL.md",[145],{"categoryNo":146,"categoryName":147,"taxonomyType":120,"taxonomyTypeDesc":121},"01JBSEEDCAT000000000000021","资料检索与研究",[149,150,151,152,153,154,155,23,156,137,21,157,95,158,159],"arxiv paper reviews","reviews","arxiv","paper","crawler","papers","comments","fetch","read","http","via",{"skillNo":161,"name":141,"nameZh":6,"description":142,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":162,"categories":163,"keywords":165,"publishedAt":38},"01M6G8HQJ7N7WMY6YV7D7SSJ16","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzxrys\u002Farxiv-paper-reviews\u002FSKILL.md",[164],{"categoryNo":146,"categoryName":147,"taxonomyType":120,"taxonomyTypeDesc":121},[152,151,150,149,159,154,155,23,153,137,21,156,157,95,158],{"skillNo":167,"name":168,"nameZh":6,"description":169,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":170,"categories":171,"keywords":172,"publishedAt":38},"01M6G8HM95Y64JNVFC2HN4E2BG","git-summary","Get a quick summary of the current Git repository including status, recent commits, branches, and contributors.","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzweack\u002Fgit-summary\u002FSKILL.md",[],[173,174,175,176,177,178,179,37,180,181,182,183,184],"git","summary","git summary","commits","repository","win32","status","contributors","true","quick","get","branches",{"skillNo":186,"name":187,"nameZh":6,"description":188,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":189,"categories":190,"keywords":195,"publishedAt":38},"01M6G8HGEGF05CNEJCGVYFSC4R","agent-constitution","Interact with AgentConstitution governance contracts on Base Sepolia. Check compliance, read rules, log actions, query governance state.","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fztsalexey\u002Fagent-constitution\u002FSKILL.md",[191,192],{"categoryNo":118,"categoryName":119,"taxonomyType":120,"taxonomyTypeDesc":121},{"categoryNo":193,"categoryName":194,"taxonomyType":90,"taxonomyTypeDesc":91},"01JBSEEDCAT000000000000037","律师与法务",[196,197,198,199,23,200,201,53,202,157,203,204,205,206,207,108,208,209,210],"agent constitution","constitution","contracts","tree","query","base","log","check","Rules","projects","compliance","actions","state","governance","github"]