<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Maruthi Vishnu Vardhan Reddy Rachamallu</title>
    <description>The latest articles on DEV Community by Maruthi Vishnu Vardhan Reddy Rachamallu (@rmvvreddy).</description>
    <link>https://dev.to/rmvvreddy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2486445%2F6410509c-d68a-4f1b-b5ea-7e8894c0b20e.jpg</url>
      <title>DEV Community: Maruthi Vishnu Vardhan Reddy Rachamallu</title>
      <link>https://dev.to/rmvvreddy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rmvvreddy"/>
    <language>en</language>
    <item>
      <title>Cron Job Not Running? A Checklist to Run Before Blaming cron</title>
      <dc:creator>Maruthi Vishnu Vardhan Reddy Rachamallu</dc:creator>
      <pubDate>Sat, 03 Oct 2026 12:40:27 +0000</pubDate>
      <link>https://dev.to/rmvvreddy/cron-job-not-running-a-checklist-to-run-before-blaming-cron-m5m</link>
      <guid>https://dev.to/rmvvreddy/cron-job-not-running-a-checklist-to-run-before-blaming-cron-m5m</guid>
      <description>&lt;p&gt;The job was supposed to run at 02:15. It’s 09:40. Finance wants the report. Cron has strong opinions and a tiny environment. Most “cron isn’t working” tickets are PATH, permissions, wrong user, or the daemon quietly not running.&lt;/p&gt;

&lt;p&gt;Here’s the checklist I use before blaming the scheduler gods. Keep chai nearby — this is usually a 15-minute mystery once you’re methodical.&lt;/p&gt;

&lt;p&gt;Automation done well is a bridge into DevOps habits — after you fix today’s crontab, skim the &lt;a href="https://sysadmin.co.in/sysadmin-to-devops-2026-roadmap/" rel="noopener noreferrer"&gt;SysAdmin → DevOps roadmap&lt;/a&gt; for moving critical jobs into reviewed, logged pipelines.&lt;/p&gt;

&lt;h2&gt;
  
  
  1) Is cron even running?
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl status cron          &lt;span class="c"&gt;# Debian/Ubuntu&lt;/span&gt;
systemctl status crond         &lt;span class="c"&gt;# RHEL-family&lt;/span&gt;
ps aux | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'[c]ron'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6o9cv34zh9yqcbhj9cfv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6o9cv34zh9yqcbhj9cfv.png" alt="Terminal showing systemctl is-enabled and is-active for cron both positive"&gt;&lt;/a&gt;&lt;br&gt;
Two quick checks: is the cron service enabled at boot, and is it running right now? Real output from an Ubuntu 24.04 server.&lt;/p&gt;

&lt;p&gt;If the unit failed, fix that first with normal &lt;a href="https://sysadmin.co.in/systemd-service-failed-to-start/" rel="noopener noreferrer"&gt;systemd + journalctl&lt;/a&gt; technique.&lt;/p&gt;
&lt;h2&gt;
  
  
  2) Which crontab did you edit?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;crontab -e&lt;/code&gt; — current user’s crontab&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sudo crontab -e&lt;/code&gt; — root’s crontab&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/etc/crontab&lt;/code&gt; and &lt;code&gt;/etc/cron.d/*&lt;/code&gt; — system files (they have a &lt;strong&gt;user&lt;/strong&gt; field)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/etc/cron.daily&lt;/code&gt; etc. — run-parts scripts (must be executable, no dots in some distros’ run-parts rules)
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;crontab &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;crontab &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /etc/cron.d/ /etc/cron.daily/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Editing the wrong file is embarrassingly common. No shame — just check.&lt;/p&gt;
&lt;h2&gt;
  
  
  3) Timing syntax and timezones
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# m h dom mon dow command&lt;/span&gt;
15 2 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /usr/local/bin/backup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Confirm the server timezone:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;timedatectl
&lt;span class="nb"&gt;date&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;UTC vs Asia/Kolkata has ruined more “it didn’t fire” threads than bad asterisks. Containers may differ from the host.&lt;/p&gt;

&lt;h2&gt;
  
  
  4) Environment: PATH and shell
&lt;/h2&gt;

&lt;p&gt;Cron gives you a minimal environment. That fancy script that works interactively fails because &lt;code&gt;python&lt;/code&gt; isn’t on cron PATH.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rules that prevent pain:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Absolute paths to binaries and scripts&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;PATH=&lt;/code&gt; at the top of the crontab if needed&lt;/li&gt;
&lt;li&gt;Source a known env file explicitly inside the script&lt;/li&gt;
&lt;li&gt;Don’t rely on aliases or interactive shell functions
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;15 2 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /usr/bin/flock &lt;span class="nt"&gt;-n&lt;/span&gt; /tmp/backup.lock /usr/local/bin/backup.sh &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;/var/log/backup.log 2&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;flock&lt;/code&gt; prevents overlapping runs. Redirect stdout/stderr to a log you actually look at.&lt;/p&gt;

&lt;h2&gt;
  
  
  5) Permissions, SELinux, and executable bits
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; /usr/local/bin/backup.sh
&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; thecronuser /usr/local/bin/backup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run as the same user cron uses. If that fails, cron will fail too. SELinux denials show up in audit logs — don’t disable SELinux to “make cron work”; fix the context.&lt;/p&gt;

&lt;p&gt;Scripts under &lt;code&gt;/etc/cron.daily&lt;/code&gt; need to be executable and often must &lt;strong&gt;not&lt;/strong&gt; have extensions depending on run-parts behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  6) Percent signs, newlines, and special characters
&lt;/h2&gt;

&lt;p&gt;In crontab, &lt;code&gt;%&lt;/code&gt; becomes a newline unless escaped. Quoting matters. If your command looks like line noise, refresh &lt;a href="https://sysadmin.co.in/linux-special-characters/" rel="noopener noreferrer"&gt;Linux special characters&lt;/a&gt; and put complexity inside a script file instead of an inline novel.&lt;/p&gt;

&lt;h2&gt;
  
  
  7) Mail, pam, and silent failure
&lt;/h2&gt;

&lt;p&gt;Cron traditionally mails output to the user. If mail isn’t configured, you see nothing. Prefer explicit logs.&lt;/p&gt;

&lt;p&gt;Also check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep &lt;/span&gt;CRON /var/log/syslog   &lt;span class="c"&gt;# Debian-ish&lt;/span&gt;
&lt;span class="nb"&gt;grep &lt;/span&gt;CROND /var/log/cron    &lt;span class="c"&gt;# RHEL-ish&lt;/span&gt;
journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-b&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Did cron even attempt to start the job? If there’s no line at the scheduled time, you’re looking at schedule/daemon/user issues. If there is a line and the job exits non-zero, debug the script.&lt;/p&gt;

&lt;h2&gt;
  
  
  8) Disk full and readonly filesystems
&lt;/h2&gt;

&lt;p&gt;Cron can’t write logs or temp files if the disk is full. If jobs vanish mysteriously after a growth spurt, run the &lt;a href="https://sysadmin.co.in/linux-disk-full-df-du-lsof-inodes/" rel="noopener noreferrer"&gt;disk full checklist&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  9) Maybe you want a systemd timer
&lt;/h2&gt;

&lt;p&gt;For new work on systemd distros, timers give better logging, dependencies, and &lt;code&gt;systemctl list-timers&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl list-timers &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cron still fine for simple schedules. Timers shine when you care about catch-up (&lt;code&gt;Persistent=true&lt;/code&gt;) and unit dependency graphs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pro checklist (copy into the ticket)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;crond/cron service active&lt;/li&gt;
&lt;li&gt;correct crontab / cron.d entry&lt;/li&gt;
&lt;li&gt;time zone understood&lt;/li&gt;
&lt;li&gt;absolute paths + logging redirect&lt;/li&gt;
&lt;li&gt;manual run as cron user succeeds&lt;/li&gt;
&lt;li&gt;logs show execution attempt&lt;/li&gt;
&lt;li&gt;exit code / script error fixed&lt;/li&gt;
&lt;li&gt;prevent overlap with flock&lt;/li&gt;
&lt;li&gt;monitoring: alert if the job’s output artifact is stale&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Security note: cron as root with world-writable scripts is a gift to attackers. Keep scripts locked down the same way you treat &lt;a href="https://sysadmin.co.in/ssh-hardening-checklist-production-linux-2026/" rel="noopener noreferrer"&gt;SSH access&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Worked example: backup script “runs” but empty files
&lt;/h2&gt;

&lt;p&gt;Cron line fires; logs show the script started; backup size is 0 bytes. Manual run as root works. Cause: cron user lacked permission to read the data directory; the script ignored &lt;code&gt;set -e&lt;/code&gt; and “succeeded.”&lt;/p&gt;

&lt;p&gt;Fixes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;set -euo pipefail&lt;/code&gt; at top of bash scripts&lt;/li&gt;
&lt;li&gt;Check exit codes of mysqldump/tar explicitly&lt;/li&gt;
&lt;li&gt;Alert if backup artifact size &amp;lt; threshold or mtime too old&lt;/li&gt;
&lt;li&gt;Run verification restores monthly (tiny sample)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Gotchas beyond the basics
&lt;/h2&gt;

&lt;p&gt;If the checklist above found nothing, look at these.&lt;/p&gt;

&lt;h3&gt;
  
  
  Crontab environment gotchas beyond PATH
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;HOME&lt;/code&gt; may differ&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SHELL&lt;/code&gt; defaults to &lt;code&gt;/bin/sh&lt;/code&gt; (dash on Debian) — bashisms fail&lt;/li&gt;
&lt;li&gt;Locale variables missing → weird sorting/date formats&lt;/li&gt;
&lt;li&gt;Secrets available in interactive pam sessions but not cron
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /usr/bin/env &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/cron-env.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compare that file to your interactive &lt;code&gt;env&lt;/code&gt;. Remove the debug line afterward.&lt;/p&gt;

&lt;h3&gt;
  
  
  Distributed systems note
&lt;/h3&gt;

&lt;p&gt;Never schedule the same critical job on five app nodes without locking or a leader. You’ll get five overlapping dumps and a sad database. Use a single scheduler host, systemd timer on one node, or an orchestrator.&lt;/p&gt;

&lt;h3&gt;
  
  
  Migrating a job to a timer
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;backup.service&lt;/code&gt; (oneshot) + &lt;code&gt;backup.timer&lt;/code&gt;. Benefits: clearer logs, dependencies, &lt;code&gt;systemctl list-timers&lt;/code&gt;, and consistent unit permissions. Keep cron for simple legacy jobs if you must — but don’t run both for the same task.&lt;/p&gt;

&lt;h3&gt;
  
  
  Observability for scheduled work
&lt;/h3&gt;

&lt;p&gt;A job without a freshness check is a wish. Minimum: dead man’s switch (Healthchecks.io, Prometheus pushgateway + alert, or “file mtime older than 26h”). Cron failing silently is how backups die for three months unnoticed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Daylight saving and odd schedules
&lt;/h3&gt;

&lt;p&gt;Jobs scheduled in the DST gap may skip or double depending on the cron implementation and timezone. Prefer UTC on servers when your org can handle it psychologically — or document local-time behavior during transitions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrappers that save careers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail
&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
&lt;span class="nb"&gt;cd&lt;/span&gt; /opt/jobs/backup
/usr/bin/flock &lt;span class="nt"&gt;-n&lt;/span&gt; /run/backup.lock /opt/jobs/backup/run.sh | /usr/bin/logger &lt;span class="nt"&gt;-t&lt;/span&gt; backup-job
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Call that wrapper from cron with an absolute path. Logging to syslog/journal means you can &lt;code&gt;journalctl -t backup-job&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Permissions on /etc/cron.d files
&lt;/h3&gt;

&lt;p&gt;Some distros ignore cron.d files that are group/world writable or have wrong names. If your drop-in never runs, check mode &lt;code&gt;644&lt;/code&gt;, root ownership, and no dots in the filename when run-parts is involved for daily directories.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final sanity printout
&lt;/h3&gt;

&lt;p&gt;At the scheduled minute, watch live:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;journalctl &lt;span class="nt"&gt;-f&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; cron
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If nothing appears, the schedule/daemon/user path is wrong. If something appears and the job fails, debug the script — not cron itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practice
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Lab exercise
&lt;/h3&gt;

&lt;p&gt;Create a user crontab that runs every minute and appends UTC timestamps to &lt;code&gt;~/cron-lab.log&lt;/code&gt;. Watch it work. Break PATH by calling a bare command that only exists in your interactive profile. Fix with absolute paths. Add flock. Remove the every-minute job when finished so you don’t wake up to a gigabyte log.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why does it run manually but not from cron?
&lt;/h3&gt;

&lt;p&gt;Environment (PATH, cwd, secrets), different user, or missing tty assumptions. Log env from the script with &lt;code&gt;env&lt;/code&gt; when debugging.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do cron jobs run when the server was off?
&lt;/h3&gt;

&lt;p&gt;Classic cron: no (missed jobs are missed). anacron/systemd Persistent timers: can catch up. Know which you have.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I run every 5 minutes?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*/5 * * * * /path/to/job
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Is &lt;a class="mentioned-user" href="https://dev.to/reboot"&gt;@reboot&lt;/a&gt; reliable?
&lt;/h3&gt;

&lt;p&gt;Mostly, after local filesystems are up — but network may not be. For networky jobs, prefer systemd with &lt;code&gt;After=network-online.target&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should secrets live in the crontab?
&lt;/h3&gt;

&lt;p&gt;No. Use a root-only env file or a secrets manager. Crontab is often readable by the user and ends up in backups.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://man7.org/linux/man-pages/man5/crontab.5.html" rel="noopener noreferrer"&gt;crontab(5) manual page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.freedesktop.org/software/systemd/man/latest/systemd.special.html" rel="noopener noreferrer"&gt;systemd.special manual page&lt;/a&gt;, for &lt;code&gt;network-online.target&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;This article first appeared on &lt;a href="https://sysadmin.co.in/cron-job-not-running/" rel="noopener noreferrer"&gt;sysadmin.co.in&lt;/a&gt;. The original has the latest updates.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>sysadmin</category>
      <category>devops</category>
      <category>bash</category>
    </item>
  </channel>
</rss>
