{
  "id": "cron-jobs",
  "title": "Cron Jobs & Scheduled Tasks",
  "category": "sysadmin",
  "tags": ["cron", "crontab", "schedule", "backup", "automation"],
  "updated": "2025-12-01",
  "summary": "Cron syntax, user vs system crons, and common failure modes.",
  "sections": [
    {
      "heading": "Cron Syntax",
      "body": "<p>A crontab entry has five time fields followed by the command:</p>",
      "code": "# ┌─── minute (0–59)\n# │ ┌─── hour (0–23)\n# │ │ ┌─── day of month (1–31)\n# │ │ │ ┌─── month (1–12)\n# │ │ │ │ ┌─── day of week (0–7, 0 and 7 are Sunday)\n# │ │ │ │ │\n  * * * * *   /path/to/command\n\n# Examples:\n0 2 * * *     /usr/local/bin/backup.sh    # 2am every day\n*/15 * * * *  /usr/local/bin/check.sh     # every 15 minutes\n0 0 1 * *     /usr/local/bin/monthly.sh   # midnight on the 1st"
    },
    {
      "heading": "User Crontabs",
      "body": "<p>Each user can have their own crontab. Commands run as that user.</p>",
      "code": "crontab -e           # edit your crontab\ncrontab -l           # list your crontab\ncrontab -l -u alice  # list alice's crontab (root only)\ncrontab -r           # delete your crontab (dangerous—no confirmation)"
    },
    {
      "heading": "System Cron Directories",
      "body": "<p>Scripts dropped into these directories run at the corresponding interval without needing a crontab entry:</p>",
      "code": "/etc/cron.daily/\n/etc/cron.weekly/\n/etc/cron.monthly/\n/etc/cron.hourly/\n\n# Scripts here must be executable and owned by root.\n# They must NOT have a file extension—run-parts ignores files with dots in the name."
    },
    {
      "heading": "Ownership and the PATH Problem",
      "body": "<p>Two common failure modes:</p><p><strong>Wrong owner:</strong> A cron script in <code>/etc/cron.daily/</code> must be owned by root. If it is owned by another user, run-parts may skip it.</p><p><strong>Missing PATH:</strong> Cron does not source <code>.bashrc</code> or <code>.profile</code>. Commands that work interactively may fail in cron because the PATH only contains <code>/usr/bin:/bin</code>. Always use full paths in cron scripts, or set PATH explicitly at the top of the script.</p>",
      "code": "#!/bin/bash\nPATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\n..."
    },
    {
      "heading": "Checking If a Cron Ran",
      "body": "",
      "code": "# Check syslog or the cron-specific log\ngrep CRON /var/log/syslog | tail -20\ncat /var/log/cron.log       # if separate cron log is configured\n\n# Check journald\njournalctl -u cron --since \"1 hour ago\""
    },
    {
      "heading": "Capturing Cron Output",
      "body": "<p>By default, cron mails output to the user. On servers with no mail configured, errors disappear silently. Redirect to a log file instead:</p>",
      "code": "0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1"
    }
  ]
}
