cron-scheduling
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.
What this skill does
# Cron & Scheduling Schedule and manage recurring tasks. Covers cron syntax, crontab management, systemd timers, one-off scheduling, timezone handling, monitoring, and common failure patterns. ## When to Use - 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 ## Cron Syntax ### The five fields ``` ┌───────── 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 ``` ### Common schedules ```bash # 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 ``` ### Special strings (shorthand) ```bash @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 * * * * ``` ## Crontab Management ```bash # 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 ``` ### Crontab best practices ```bash # Set PATH explicitly (cron has minimal PATH) PATH=/usr/local/bin:/usr/bin:/bin # Set MAILTO for error notifications [email protected] # Set shell explicitly SHELL=/bin/bash # Full crontab example PATH=/usr/local/bin:/usr/bin:/bin [email protected] 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" ``` ## Systemd Timers ### Create a timer (modern cron replacement) ```ini # /etc/systemd/system/backup.service [Unit] Description=Daily backup [Service] Type=oneshot ExecStart=/opt/scripts/backup.sh User=backup StandardOutput=journal StandardError=journal ``` ```ini # /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 ``` ```bash # 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 ``` ### OnCalendar syntax ```ini # 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" ``` ### Advantages over cron ``` Systemd timers vs cron: + Logs in journald (journalctl -u service-name) + Persistent: catches up on missed runs after reboot + RandomizedDelaySec: prevents thundering herd + Dependencies: can depend on network, mounts, etc. + Resource limits: CPUQuota, MemoryMax, etc. + No lost-email problem (MAILTO often misconfigured) - More files to create (service + timer) - More verbose configuration ``` ## One-Off Scheduling ### at (run once at a specific time) ```bash # 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" [email protected] > <Ctrl+D> # List pending jobs atq # View job details at -c <job-number> # Remove a job atrm <job-number> ``` ### sleep-based (simplest) ```bash # Run something after a delay (sleep 3600 && /opt/scripts/task.sh) & # With nohup (survives logout) nohup bash -c "sleep 7200 && /opt/scripts/task.sh" & ``` ## Timezone Handling ```bash # 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 ``` ### DST pitfalls ``` 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 ``` ## Monitoring and Debugging ### Why didn't my cron job run? ```bash # 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 ``` ### Job wrapper with logging and alerting ```bash #!/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" |
Related in Productivity
gitea-workflow
IncludedOrchestrate agile development workflows for Gitea repositories using the tea CLI. Use when working with Gitea-hosted repos and asking to 'run the workflow', 'continue working', 'what's next', 'complete the task cycle', 'start my day', 'end the sprint', 'implement the next task', or wanting guided step-by-step development assistance. Keywords: workflow, orchestrate, agile, task cycle, sprint, daily, implement, review, PR, standup, retrospective, gitea, tea.
microsoft-graph-gateway
IncludedRoute Microsoft Graph work in this workspace. Use when users want to read or write Outlook mail, calendar events, contacts, OneDrive or SharePoint files, Teams, Planner, To Do, users, groups, directory data, or arbitrary Microsoft Graph endpoints from VS Code. Prefer WorkIQ for common read scenarios. Use Microsoft Graph for write actions and gap-read scenarios that need exact Graph properties, filters, permissions, or endpoints.
copilotkit
IncludedUse when building with CopilotKit — setup, development, integrations, debugging, upgrading, or contributing. Routes to the appropriate specialized skill based on the task.
wordly-wisdom
IncludedProvides calibrated decision analysis using Charlie Munger-style multiple mental models, inversion, incentive mapping, circle-of-competence checks, misjudgment audits, second-order effects, and forecast updates. Use when the user asks for an oracle take, a hard call, a decision memo, a premortem, an outside view, a red-team, a sanity-check, what am I missing, think this through, or wants a strategy, hire, investment, plan, product, partnership, or major life choice analysed. Avoid for simple factual lookups or time-sensitive legal, medical, or market questions without fresh evidence.
swain-session
IncludedSession management and project status dashboard. Owns the full session lifecycle (start/work/close/resume), focus lane, bookmarks, worktree detection, and tab naming. Also serves as the project status dashboard — shows active epics, progress, actionable next steps, blocked items, tasks, GitHub issues, and recommendations. Worktree creation is deferred to swain-do task dispatch (SPEC-195). Triggers on: 'session', 'status', 'what's next', 'dashboard', 'overview', 'where are we', 'what should I work on', 'show me priorities', 'bookmark', 'focus on', 'session info'.
gandi
IncludedComprehensive Gandi domain registrar integration for domain and DNS management. Register and manage domains, create/update/delete DNS records (A, AAAA, CNAME, MX, TXT, SRV, and more), configure email forwarding and aliases, check SSL certificate status, create DNS snapshots for safe rollback, bulk update zone files, and monitor domain expiration. Supports multi-domain management, zone file import/export, and automated DNS backups. Includes both read-only and destructive operations with safety controls.