crontab Command in Linux: Complete Guide to Scheduling Cron Jobs and Parameters

crontab command in Linux and it perimeters

If there’s one Linux command that’s quietly running more of the internet than people realize, it’s crontab. Log rotation, database backups, certificate renewals, report generation, cache warming — a huge share of “boring but essential” server work runs on cron, and crontab is how you tell it what to do and when. I’ve relied on it for years to keep servers self-maintaining, and once you understand the time syntax and the daemon behind it, it becomes one of the most dependable tools in your admin toolkit.

What crontab Actually Is

crontab (short for “cron table”) is the command used to install, edit, list, and remove the schedule of jobs that the cron daemon (crond on RHEL-family systems, cron on Debian-family systems) executes automatically at specified times. Cron itself runs constantly in the background as a system service, waking up every minute to check whether any scheduled job matches the current time.

Each user on the system can have their own personal crontab file, and there’s also a system-wide crontab (/etc/crontab) plus drop-in directories (/etc/cron.d/) for packages and admins to install jobs without touching a single shared file.

Basic Syntax

crontab [-u user] file
crontab [-u user] [-l | -r | -e] [-i] [-s]
  • file — install a crontab from the given file (replacing any existing one).
  • -l — list the current crontab for the invoking user (or -u user, if root).
  • -e — edit the current crontab interactively (uses $EDITOR or $VISUAL).
  • -r — remove the current crontab entirely.
  • -i — used with -r, prompts for confirmation before removing.
  • -u user — operate on another user’s crontab (root only).
  • -s — (on some implementations) append a SELinux security context.

You almost never edit crontab files directly on disk — you use crontab -e, which validates syntax and handles locking safely.

The Five-Field Time Syntax

This is the part everyone has to memorize eventually:

* * * * * command-to-run
│ │ │ │ │
│ │ │ │ └── day of week (0–7, both 0 and 7 = Sunday)
│ │ │ └──── month (1–12)
│ │ └────── day of month (1–31)
│ └──────── hour (0–23)
└────────── minute (0–59)

An asterisk (*) means “every possible value for this field.”

Special Characters

  • * — every value.
  • , — a list: 1,15,30 means at those specific values.
  • - — a range: 9-17 means 9 through 17 inclusive.
  • / — a step: */15 means every 15 units; 9-17/2 means every 2 hours between 9 and 17.
  • @reboot, @daily, @hourly, @weekly, @monthly, @yearly/@annually — special shortcut strings supported by most cron implementations (Vixie cron, cronie) in place of the five fields.

Practical Examples

# Every minute
* * * * * /usr/local/bin/heartbeat.sh

# Every day at 2:30 AM
30 2 * * * /usr/local/bin/backup.sh

# Every 15 minutes
*/15 * * * * /usr/local/bin/check_disk.sh

# Every weekday (Mon–Fri) at 9 AM
0 9 * * 1-5 /usr/local/bin/morning_report.sh

# First day of every month at midnight
0 0 1 * * /usr/local/bin/monthly_billing.sh

# Every 6 hours
0 */6 * * * /usr/local/bin/sync_data.sh

# At system boot
@reboot /usr/local/bin/startup_tasks.sh

# Twice a day, at 6 AM and 6 PM
0 6,18 * * * /usr/local/bin/twice_daily.sh

Editing Your Crontab

crontab -e

The first time you run this as a given user, it may ask which editor to use:

no crontab for john - using an empty one

Select an editor.  To change later, run 'select-editor'.
  1. /bin/nano
  2. /usr/bin/vim.basic
  3. /bin/ed
Choose 1-3 [1]:

Once saved, cron validates the syntax before installing it — if there’s a malformed line, it’ll usually refuse to save and show you an error, which is one reason crontab -e is safer than hand-editing the underlying spool files.

Listing and Removing

crontab -l          # show your own crontab
crontab -u alice -l  # show alice's crontab (root only)
crontab -r          # delete your entire crontab (no confirmation!)
crontab -i -r        # delete with a confirmation prompt

crontab -r is genuinely dangerous — there’s no undo unless you kept a backup. I always run crontab -l > ~/crontab_backup_$(date +%F).txt before making risky changes.

Where Crontabs Actually Live

Personal crontabs (installed via crontab -e) are stored per-user, typically at:

/var/spool/cron/crontabs/<username>     # Debian/Ubuntu
/var/spool/cron/<username>              # RHEL/CentOS/Fedora

You should never edit these files directly with a text editor — always go through the crontab command, because it re-validates syntax and notifies the cron daemon of changes properly.

System-Wide Crontab and cron.d

Besides per-user crontabs, there’s /etc/crontab, which has one extra field compared to a personal crontab — the user to run the job as:

# /etc/crontab
# m h dom mon dow user  command
17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
25 6    * * *   root    test -x /usr/sbin/anacron || run-parts /etc/cron.daily

Packages typically install their own scheduled jobs as individual files under /etc/cron.d/, using this same six-field (with user) format, rather than touching /etc/crontab directly. This is the pattern you should follow for jobs that should be system-managed rather than tied to one user’s personal crontab:

sudo tee /etc/cron.d/backup-job <<'EOF'
# Nightly backup job
30 1 * * * root /usr/local/bin/nightly_backup.sh >> /var/log/nightly_backup.log 2>&1
EOF

There are also the convenience directories /etc/cron.hourly/, /etc/cron.daily/, /etc/cron.weekly/, /etc/cron.monthly/ — you drop an executable script in there (no crontab syntax needed) and run-parts (invoked from /etc/crontab or via anacron) executes everything inside on that cadence.

Controlling Who Can Use crontab

Two files gate access, checked in this order:

  • /etc/cron.allow — if it exists, only users listed here may use crontab.
  • /etc/cron.deny — if cron.allow doesn’t exist, users listed here are blocked, everyone else is allowed.

If neither file exists, behavior is distro-dependent — some allow everyone, some (notably Debian) restrict to root by default. Check with:

ls -la /etc/cron.allow /etc/cron.deny 2>/dev/null

How Cron Works Internally

The cron daemon starts at boot as a system service (systemctl status cron or systemctl status crond). Once running, it wakes up once per minute, reads every installed crontab (personal spool files plus /etc/crontab and /etc/cron.d/*), and compares the current minute/hour/day/month/weekday against each job’s schedule fields. Any job that matches gets forked and executed via /bin/sh (unless SHELL= is overridden inside the crontab), with output — both stdout and stderr — mailed to the crontab owner via the local mail system, unless redirected.

Cron does not reload crontab files continuously watching for changes in real time in older implementations, but modern cron daemons (like cronie, common on RHEL/Fedora) do detect modification timestamps on the spool directory and reload automatically — either way, crontab -e triggers an immediate reload as part of installing the new file, so you don’t need to restart the service after editing.

Environment Inside Cron Jobs

This is the single most common source of “it works when I run it manually, but not from cron” bugs. Cron jobs run with a very minimal environment — typically just:

SHELL=/bin/sh
PATH=/usr/bin:/bin
HOME=<user's home>
LOGNAME=<username>

Your ~/.bashrc, ~/.bash_profile, and any PATH modifications you rely on interactively are not loaded. This is why scripts that call python3, node, aws, or anything installed outside /usr/bin//bin mysteriously fail from cron.

Fixes:

# Explicitly set PATH at the top of the crontab
PATH=/usr/local/bin:/usr/bin:/bin

# Or set it inside the script itself
0 3 * * * /usr/local/bin/backup.sh
#!/bin/bash
# backup.sh
export PATH="/usr/local/bin:/usr/bin:/bin"
# ...

You can also set other variables at the top of a crontab, like MAILTO= (who gets the output mail, blank to disable) and SHELL=.

Real-World Examples and Shell Scripting Patterns

Database backup with logging and rotation:

0 2 * * * /usr/local/bin/pg_backup.sh >> /var/log/pg_backup.log 2>&1
#!/bin/bash
# pg_backup.sh
set -euo pipefail
BACKUP_DIR="/var/backups/postgres"
DATE=$(date +%F)
pg_dump mydb | gzip > "${BACKUP_DIR}/mydb_${DATE}.sql.gz"
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +14 -delete

Preventing overlapping runs with flock:

*/5 * * * * /usr/bin/flock -n /tmp/sync.lock /usr/local/bin/sync_data.sh

This is essential for jobs that might occasionally run longer than their own interval — without a lock, you can end up with dozens of overlapping processes hammering the same resource.

Sending output only on failure instead of every run:

#!/bin/bash
OUTPUT=$(/usr/local/bin/some_task.sh 2>&1)
if [ $? -ne 0 ]; then
    echo "$OUTPUT" | mail -s "Cron job failed: some_task.sh" admin@example.com
fi

Troubleshooting

Job doesn’t run at all:

  • Check the cron service is active: systemctl status cron / systemctl status crond.
  • Check /etc/cron.allow / /etc/cron.deny isn’t blocking the user.
  • Check syslog: grep CRON /var/log/syslog (Debian/Ubuntu) or grep CRON /var/log/cron (RHEL/CentOS).

Job runs manually but fails from cron:

  • Almost always a PATH or environment variable issue (see above).
  • Check for relative paths in the script — always use absolute paths in cron jobs.

No output/errors visible:

  • By default, output is emailed via the local MTA. If no mail system is configured, output silently disappears. Redirect explicitly: >> /var/log/myjob.log 2>&1.

Job seems to run twice:

  • Check you don’t have the same job defined in both a personal crontab and /etc/cron.d/.

Performance and Reliability Considerations

  • For jobs that must run even if the machine was off at the scheduled time (laptops, machines that sleep), consider anacron instead of plain cron — cron simply skips missed runs.
  • Stagger heavy jobs (backups, reports) rather than bunching everything at 0 0 * * * — server load spikes at midnight are a very real, very common self-inflicted problem.
  • Use flock or PID-file locking for any job whose runtime could exceed its own schedule interval.
  • Always log output somewhere durable; silent cron failures are one of the most common causes of “we didn’t notice the backup stopped working three weeks ago.”

Comparison to Related Tools

  • at — schedules a one-time job for a specific future time, rather than a recurring schedule. Good for “run this once tonight,” bad for anything recurring.
  • anacron — designed for machines that aren’t always on; ensures daily/weekly/monthly jobs still run even if the exact time was missed, catching up on next boot.
  • systemd timers — the modern alternative on systemd-based distros; more powerful (dependency-aware, logs via journalctl, can trigger on events besides time), but with a steeper learning curve and more verbose unit-file setup. Cron remains popular for its simplicity and near-universal familiarity.

crontab‘s five-field syntax and general behavior are consistent across Debian, Ubuntu, RHEL, CentOS, Fedora, SUSE, and Arch, since almost all of them ship either Vixie cron or its descendant cronie. Minor differences show up in default cron.allow/cron.deny behavior and in exact log file locations.

Summary

crontab is deceptively simple on the surface — five fields and a command — but the reliability of an entire server’s automation often rests on understanding its environment quirks, locking behavior, and where jobs actually live on disk. Get comfortable with crontab -e, always use absolute paths and explicit PATH settings, log everything, and use flock for anything that could overlap — and cron will quietly keep your systems running for years without complaint.

References

  • man crontab, man 5 crontab, man cron on your local system
  • Vixie Cron and cronie project documentation
  • Debian Administrator’s Handbook, “Task Scheduling: cron and atd”
  • Red Hat Enterprise Linux documentation, “Automating System Tasks”

Total
0
Shares

Leave a Reply

Previous Post
atrm command in Linux and it perimeters

atrm Command in Linux: Complete Guide to Removing Scheduled Jobs and Parameters

Next Post
useradd command in Linux and it perimeters

useradd Command in Linux: Complete Guide to Creating New Users and Parameters

Related Posts