Requirements
- Target platform
- OpenClaw
- Install method
- Manual import
- Extraction
- Extract archive
- Prerequisites
- OpenClaw
- Primary doc
- SKILL.md
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.
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.
Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.
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.
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.
Schedule and manage recurring tasks. Covers cron syntax, crontab management, systemd timers, one-off scheduling, timezone handling, monitoring, and common failure patterns.
Running scripts on a schedule (backups, reports, cleanup) Setting up systemd timers (modern cron alternative) Debugging why a scheduled job didn't run Handling timezones in scheduled tasks Monitoring and alerting on job failures Running one-off delayed commands
โโโโโโโโโโ minute (0-59) โ โโโโโโโโ hour (0-23) โ โ โโโโโโ day of month (1-31) โ โ โ โโโโ month (1-12 or JAN-DEC) โ โ โ โ โโ day of week (0-7, 0 and 7 = Sunday, or SUN-SAT) โ โ โ โ โ * * * * * command
# Every minute * * * * * /path/to/script.sh # Every 5 minutes */5 * * * * /path/to/script.sh # Every hour at :00 0 * * * * /path/to/script.sh # Every day at 2:30 AM 30 2 * * * /path/to/script.sh # Every Monday at 9:00 AM 0 9 * * 1 /path/to/script.sh # Every weekday at 8:00 AM 0 8 * * 1-5 /path/to/script.sh # First day of every month at midnight 0 0 1 * * /path/to/script.sh # Every 15 minutes during business hours (Mon-Fri 9-17) */15 9-17 * * 1-5 /path/to/script.sh # Twice a day (9 AM and 5 PM) 0 9,17 * * * /path/to/script.sh # Every quarter (Jan, Apr, Jul, Oct) on the 1st at midnight 0 0 1 1,4,7,10 * /path/to/script.sh # Every Sunday at 3 AM 0 3 * * 0 /path/to/script.sh
@reboot /path/to/script.sh # Run once at startup @yearly /path/to/script.sh # 0 0 1 1 * @monthly /path/to/script.sh # 0 0 1 * * @weekly /path/to/script.sh # 0 0 * * 0 @daily /path/to/script.sh # 0 0 * * * @hourly /path/to/script.sh # 0 * * * *
# Edit current user's crontab crontab -e # List current crontab crontab -l # Edit another user's crontab (root) sudo crontab -u www-data -e # Remove all cron jobs (be careful!) crontab -r # Install crontab from file crontab mycrontab.txt # Backup crontab crontab -l > crontab-backup-$(date +%Y%m%d).txt
# Set PATH explicitly (cron has minimal PATH) PATH=/usr/local/bin:/usr/bin:/bin # Set MAILTO for error notifications MAILTO=admin@example.com # Set shell explicitly SHELL=/bin/bash # Full crontab example PATH=/usr/local/bin:/usr/bin:/bin MAILTO=admin@example.com SHELL=/bin/bash # Backups 0 2 * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1 # Cleanup old logs 0 3 * * 0 find /var/log/myapp -name "*.log" -mtime +30 -delete # Health check */5 * * * * /opt/scripts/healthcheck.sh || /opt/scripts/alert.sh "Health check failed"
# /etc/systemd/system/backup.service [Unit] Description=Daily backup [Service] Type=oneshot ExecStart=/opt/scripts/backup.sh User=backup StandardOutput=journal StandardError=journal # /etc/systemd/system/backup.timer [Unit] Description=Run backup daily at 2 AM [Timer] OnCalendar=*-*-* 02:00:00 Persistent=true RandomizedDelaySec=300 [Install] WantedBy=timers.target # Enable and start the timer sudo systemctl daemon-reload sudo systemctl enable --now backup.timer # Check timer status systemctl list-timers systemctl list-timers --all # Check last run systemctl status backup.service journalctl -u backup.service --since today # Run manually (for testing) sudo systemctl start backup.service # Disable timer sudo systemctl disable --now backup.timer
# Systemd calendar expressions # Daily at midnight OnCalendar=daily # or: OnCalendar=*-*-* 00:00:00 # Every Monday at 9 AM OnCalendar=Mon *-*-* 09:00:00 # Every 15 minutes OnCalendar=*:0/15 # Weekdays at 8 AM OnCalendar=Mon..Fri *-*-* 08:00:00 # First of every month OnCalendar=*-*-01 00:00:00 # Every 6 hours OnCalendar=0/6:00:00 # Specific dates OnCalendar=2026-02-03 12:00:00 # Test calendar expressions systemd-analyze calendar "Mon *-*-* 09:00:00" systemd-analyze calendar "*:0/15" systemd-analyze calendar --iterations=5 "Mon..Fri *-*-* 08:00:00"
# Schedule a command echo "/opt/scripts/deploy.sh" | at 2:00 AM tomorrow echo "reboot" | at now + 30 minutes echo "/opt/scripts/report.sh" | at 5:00 PM Friday # Interactive (type commands, Ctrl+D to finish) at 10:00 AM > /opt/scripts/task.sh > echo "Done" | mail -s "Task complete" admin@example.com > <Ctrl+D> # List pending jobs atq # View job details at -c <job-number> # Remove a job atrm <job-number>
# Run something after a delay (sleep 3600 && /opt/scripts/task.sh) & # With nohup (survives logout) nohup bash -c "sleep 7200 && /opt/scripts/task.sh" &
# Cron runs in the system timezone by default # Check system timezone timedatectl date +%Z # Set timezone for a specific cron job # Method 1: TZ variable in crontab TZ=America/New_York 0 9 * * * /opt/scripts/report.sh # Method 2: In the script itself #!/bin/bash export TZ=UTC # All date operations now use UTC # Method 3: Wrapper TZ=Europe/London date '+%Y-%m-%d %H:%M:%S' # List available timezones timedatectl list-timezones timedatectl list-timezones | grep America
Problem: A job scheduled for 2:30 AM may run twice or not at all during DST transitions. "Spring forward": 2:30 AM doesn't exist (clock jumps 2:00 โ 3:00) "Fall back": 2:30 AM happens twice Mitigation: 1. Schedule critical jobs outside 1:00-3:00 AM 2. Use UTC for the schedule: TZ=UTC in crontab 3. Make jobs idempotent (safe to run twice) 4. Systemd timers handle DST correctly
# 1. Check cron daemon is running systemctl status cron # Debian/Ubuntu systemctl status crond # CentOS/RHEL # 2. Check cron logs grep CRON /var/log/syslog # Debian/Ubuntu grep CRON /var/log/cron # CentOS/RHEL journalctl -u cron --since today # systemd # 3. Check crontab actually exists crontab -l # 4. Test the command manually (with cron's environment) env -i HOME=$HOME SHELL=/bin/sh PATH=/usr/bin:/bin /opt/scripts/backup.sh # If it fails here but works normally โ PATH or env issue # 5. Check permissions ls -la /opt/scripts/backup.sh # Must be executable ls -la /var/spool/cron/ # Crontab file permissions # 6. Check for syntax errors in crontab # cron silently ignores lines with errors # 7. Check if output is being discarded # By default, cron emails output. If no MTA, output is lost. # Always redirect: >> /var/log/myjob.log 2>&1
#!/bin/bash # cron-wrapper.sh โ Run a command with logging, timing, and error alerting # Usage: cron-wrapper.sh <job-name> <command> [args...] set -euo pipefail JOB_NAME="${1:?Usage: cron-wrapper.sh <job-name> <command> [args...]}" shift COMMAND=("$@") LOG_DIR="/var/log/cron-jobs" mkdir -p "$LOG_DIR" LOG_FILE="$LOG_DIR/$JOB_NAME.log" log() { echo "[$(date -u '+%Y-%m-%dT%H:%M:%SZ')] $*" >> "$LOG_FILE"; } log "START: ${COMMAND[*]}" START_TIME=$(date +%s) if "${COMMAND[@]}" >> "$LOG_FILE" 2>&1; then ELAPSED=$(( $(date +%s) - START_TIME )) log "SUCCESS (${ELAPSED}s)" else EXIT_CODE=$? ELAPSED=$(( $(date +%s) - START_TIME )) log "FAILED with exit code $EXIT_CODE (${ELAPSED}s)" # Alert (customize as needed) echo "Cron job '$JOB_NAME' failed with exit $EXIT_CODE" | \ mail -s "CRON FAIL: $JOB_NAME" admin@example.com 2>/dev/null || true exit $EXIT_CODE fi # Use in crontab: 0 2 * * * /opt/scripts/cron-wrapper.sh daily-backup /opt/scripts/backup.sh */5 * * * * /opt/scripts/cron-wrapper.sh health-check /opt/scripts/healthcheck.sh
# Prevent concurrent runs (job takes longer than interval) # Method 1: flock * * * * * flock -n /tmp/myjob.lock /opt/scripts/slow-job.sh # Method 2: In the script LOCKFILE="/tmp/myjob.lock" exec 200>"$LOCKFILE" flock -n 200 || { echo "Already running"; exit 0; } # ... do work ...
# Idempotent backup (only creates if newer than last backup) #!/bin/bash BACKUP_DIR="/backups/$(date +%Y%m%d)" [[ -d "$BACKUP_DIR" ]] && { echo "Backup already exists"; exit 0; } mkdir -p "$BACKUP_DIR" pg_dump mydb > "$BACKUP_DIR/mydb.sql" # Idempotent cleanup (safe to run multiple times) find /tmp/uploads -mtime +7 -type f -delete 2>/dev/null || true # Idempotent sync (rsync only transfers changes) rsync -az /data/ backup-server:/backups/data/
Always redirect output in cron jobs: >> /var/log/job.log 2>&1. Without this, output goes to mail (if configured) or is silently lost. 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. Use flock to prevent overlapping runs when a job might take longer than its schedule interval. Make all scheduled jobs idempotent. If a job runs twice (DST, manual trigger, crash recovery), it should produce the same result. systemd-analyze calendar is invaluable for verifying timer schedules before deploying. Never schedule critical jobs between 1:00 AM and 3:00 AM if DST applies. Use UTC schedules instead. Log the start time, end time, and exit code of every cron job. Without this, debugging failures after the fact is guesswork. Prefer systemd timers over cron for production services: you get journald logging, missed-run catchup (Persistent=true), and resource limits for free.
Code helpers, APIs, CLIs, browser automation, testing, and developer operations.
Largest current source with strong distribution and engagement signals.