Schedule It Once, Run It Forever: A DevOps Guide to Cron

Servers never sleep. Backups run at midnight. Log files rotate every Sunday. Certificates renew every 90 days. Someone has to trigger all of that. On Linux, that someone is cron.

In this post, we learn what cron is, what problems it solves, and how to write cron expressions that do exactly what we intend.


What Is Cron?

Cron is a time-based job scheduler built into Linux. It runs commands — or scripts — at times we define in advance. We define those times using a structured syntax. Cron reads our schedule and fires each command at the right moment, automatically, without human input.

Cron has been part of Unix systems since 1975. Every major Linux distribution ships with it. We do not install it. We just use it.

The cron process itself is called the cron daemon. It runs in the background under the name crond or cron, depending on the distribution. It wakes up every minute, checks for scheduled jobs, and runs any job whose time matches the current minute.


What Problems Does Cron Solve?

Without a scheduler, we face two choices: we run tasks manually, or we write custom background processes. Both options are fragile.

Manual execution fails when we forget, or when we are not available. Custom background processes take time to write, test, and maintain.

Cron solves this cleanly. Common use cases include:

  • Database backups on a nightly schedule
  • Log rotation and cleanup
  • Certificate renewal (e.g., Let's Encrypt)
  • Health checks and alerting scripts
  • Report generation and delivery
  • Cache clearing and data synchronization
  • System monitoring and disk usage checks


What Is a Crontab?

Crontab stands for cron table. It is a plain text file that holds our scheduled job definitions. Each line in the file is one scheduled job.

Every Linux user has their own crontab. The system also has a system-wide crontab. We interact with crontabs using the crontab command.

User Crontabs

Each user's crontab runs jobs under that user's permissions. We manage our own crontab with these commands:

# Open our crontab for editing
crontab -e

# List our current crontab entries
crontab -l

# Remove our entire crontab
crontab -r

 

Warning: crontab -r deletes all our scheduled jobs with no confirmation. We must be careful with this command.


When we run crontab -e, Linux opens our crontab in our default terminal editor. We add or edit job lines, save the file, and cron picks up the changes immediately. We do not reload any daemon.


The System Crontab

The system crontab lives at /etc/crontab. System packages also drop job files into /etc/cron.d/. These files include an extra field — the username — because they can run jobs as different users.

We can also drop scripts directly into these directories for simple scheduling needs:

Directory Runs
/etc/cron.hourly/ Once per hour
/etc/cron.daily/ Once per day
/etc/cron.weekly/ Once per week
/etc/cron.monthly/ Once per month


We place an executable script in the right directory. Cron runs it at the matching interval. No cron expression needed.


Cron Syntax

Every line in a crontab follows the same structure. We call it a cron expression. It has five time fields followed by the command to run.

* * * * *  command to execute
│ │ │ │ │
│ │ │ │ └── Day of week  (0–7, where 0 and 7 both mean Sunday)
│ │ │ └──── Month        (1–12)
│ │ └────── Day of month  (1–31)
│ └──────── Hour          (0–23)
└────────── Minute        (0–59)


Each field accepts specific values:

Field Allowed Values
Minute 0–59
Hour 0–23
Day of month 1–31
Month 1–12 (or jandec)
Day of week 0–7 (or sunsat; 0 and 7 both mean Sunday)


Special Characters

Cron expressions use four special characters to express complex schedules.

Character Meaning Example
* Every possible value * in the hour field means every hour
, A list of values 1,15,30 in the minute field means at minute 1, 15, and 30
- A range of values 9-17 in the hour field means every hour from 9 to 17
/ A step interval */15 in the minute field means every 15 minutes


Cron Expressions with Examples

Every Minute

* * * * *  /opt/scripts/ping.sh

Every single minute, cron runs ping.sh. We rarely use this in production. It is useful for testing that a cron job fires correctly.


Every Hour, on the Hour

0 * * * *  /opt/scripts/sync.sh

At minute 0 of every hour, cron runs sync.sh. This fires at 01:00, 02:00, 03:00, and so on.


Every Day at Midnight

0 0 * * *  /opt/scripts/backup.sh

At 00:00 every day, cron runs backup.sh. This is one of the most common schedules for nightly backup jobs.


Every Day at 2:30 AM

30 2 * * *  /opt/scripts/report.sh

At minute 30 of hour 2 every day, cron runs report.sh. We schedule resource-heavy jobs in the early morning hours when server load is lowest.


Every 15 Minutes

*/15 * * * *  /opt/scripts/healthcheck.sh

The / character sets a step. */15 in the minute field means: start at 0, then every 15 minutes. This fires at :00, :15, :30, and :45 of every hour.


Every Weekday at 8 AM

0 8 * * 1-5  /opt/scripts/send-report.sh

At 08:00 on Monday through Friday, cron runs send-report.sh. The 1-5 range in the day-of-week field covers Monday (1) through Friday (5).


Every Monday at 9 AM

0 9 * * 1  /opt/scripts/weekly-summary.sh

At 09:00 every Monday, cron fires weekly-summary.sh. We can use the number 1 or the name mon — both are valid.


First Day of Every Month at 6 AM

0 6 1 * *  /opt/scripts/monthly-cleanup.sh

At 06:00 on the 1st of every month, cron runs monthly-cleanup.sh. This is a standard schedule for monthly billing scripts, archive jobs, and data purges.


Specific Months Only

0 0 1 1,4,7,10 *  /opt/scripts/quarterly-audit.sh

At midnight on the 1st day of January, April, July, and October, cron runs quarterly-audit.sh. The , character lets us list multiple values in any field.


Every 5 Minutes, Business Hours Only

*/5 9-17 * * 1-5  /opt/scripts/monitor.sh

Every 5 minutes, between 9 AM and 5 PM, Monday through Friday, cron runs monitor.sh. We combine step values, ranges, and day-of-week filters to build precise schedules.


Special Shorthand Strings

Cron supports shorthand strings for common schedules. We use them in place of the five time fields.

Shorthand Equivalent Meaning
@reboot Runs once at system startup
@hourly 0 * * * * Runs at the start of every hour
@daily 0 0 * * * Runs at midnight every day
@weekly 0 0 * * 0 Runs at midnight every Sunday
@monthly 0 0 1 * * Runs at midnight on the 1st of every month
@yearly 0 0 1 1 * Runs at midnight on January 1st

Example — run a cleanup script once at boot:

@reboot  /opt/scripts/init-cleanup.sh


Crontab Best Practices

Always Use Absolute Paths

Cron runs with a minimal environment. It does not load our shell profile. It does not know our PATH. We will always use full paths for both the command and any files it references.

# Wrong
backup.sh

# Right
/opt/scripts/backup.sh


Redirect Output

Cron sends command output to the local system mail by default. We will redirect output to a log file instead.

0 2 * * *  /opt/scripts/backup.sh >> /var/log/backup.log 2>&1


The >> /var/log/backup.log appends stdout to a log file. The 2>&1 sends stderr to the same place. We can then check that file to confirm our jobs ran correctly.


Test Commands Manually First

Before we schedule a command, we will run it directly in our terminal. We will confirm it works with the exact path and arguments we plan to put in the crontab. This saves us from debugging a silent cron failure.

Set the MAILTO Variable

We can configure cron to email output to a specific address. We will add this line at the top of our crontab:

MAILTO=ops-team@company.com

We can also silence all email output by setting it to empty:

MAILTO=""


Set the Shell and PATH

We can define these variables at the top of our crontab to control the execution environment:

SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin


A Complete Crontab Example

Here is a realistic crontab for a DevOps engineer managing a Linux server:

# Set environment
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=""

# Health check every 5 minutes during business hours
*/5 9-17 * * 1-5  /opt/scripts/healthcheck.sh >> /var/log/healthcheck.log 2>&1

# Database backup every night at 2 AM
0 2 * * *  /opt/scripts/db-backup.sh >> /var/log/db-backup.log 2>&1

# Log rotation every Sunday at midnight
0 0 * * 0  /opt/scripts/rotate-logs.sh >> /var/log/rotate.log 2>&1

# Monthly disk usage report on the 1st at 7 AM
0 7 1 * *  /opt/scripts/disk-report.sh >> /var/log/disk-report.log 2>&1

# Certificate renewal check every day at 3 AM
0 3 * * *  /opt/scripts/cert-renew.sh >> /var/log/cert-renew.log 2>&1

# Cleanup temp files on reboot
@reboot  /opt/scripts/cleanup-tmp.sh


Summary

Cron is one of the most useful tools on a Linux server. Here is what we covered:

  • Cron is the Linux job scheduler. It runs commands on a defined time schedule.
  • Crontab is the file that holds our job definitions. We manage it with crontab -e.
  • A cron expression has five time fields: minute, hour, day of month, month, day of week.
  • Special characters — *, ,, -, / — give us precise control over scheduling.
  • Shorthand strings like @daily and @reboot cover the most common patterns.
  • We will always use absolute paths, redirect output to log files, and test commands manually before scheduling them.

In the next post, we will connect cron directly to the Mule Runtime. We will write production-grade cron jobs for log management, heap dump cleanup, and scheduled Mule app restarts — all running as a dedicated system user with safe, auditable output.

Previous Post Next Post