Linux Cron Jobs Explained: How Scheduling Works and Why Cron Jobs Fail

Glass clock, calendar, terminal window, and gears illustrating Linux cron jobs and scheduled automation Linux & Administration
Learn how cron schedules Linux commands, why cron jobs can behave differently from commands run in your terminal, and how to troubleshoot common failures.
⏱ Linux Automation Foundation
A cron job is a command attached to a calendar rule.

The difficult part is rarely typing five time fields. Reliable scheduling means understanding who runs the command, which environment it receives, where its output goes, and what happens when your assumptions differ from cron’s execution context.

Linux cron jobs let you run commands automatically on a schedule without opening a terminal. They are commonly used for maintenance scripts, reports, cleanup jobs, backups, synchronization tasks, and other recurring work on Linux servers.

A cron job that looks correct can still fail for reasons unrelated to the schedule itself. The job may receive a smaller environment than your interactive shell, use a different command path, run as a different user than expected, write output somewhere nobody checks, or start again while an earlier run is still active.

⚡ Quick Answer

A cron job combines a schedule with a command. A user crontab normally contains five time fields followed by the command to execute. The scheduler checks whether the calendar fields match and runs the command under the identity associated with that crontab.

Reliable Linux cron jobs depend on more than the schedule itself. The execution user, environment, paths, working context, and output handling all affect whether a scheduled command succeeds.

🎯 What You’ll Learn

Read cron syntax
Understand what each of the five calendar fields controls.
Predict execution
Translate schedules such as */15 * * * * into actual run times.
Understand identity
Know which user runs a job and why user and system crontabs differ.
Handle the environment
Recognize why commands that work interactively can fail under cron.
Capture evidence
Make output and errors observable instead of silently guessing.
Choose the right scheduler
Know when cron is enough and when a systemd timer may fit better.

🧠 How Linux Cron Jobs Work: Schedule → Identity → Environment → Command

Cron becomes much easier to reason about when you stop thinking of it as a mysterious background feature. It is simply a mechanism that decides when a command should be launched.

🗓
Schedule Does this minute match?
👤
Identity Which user owns the job?
🧩
Environment Which variables and paths exist?
▶
Command What actually executes?

This model explains one of the most common cron problems. A script may work perfectly when launched from your shell, but that proves only that it works with your current user, working context, variables, shell configuration, and PATH. Cron starts the command separately.

If you have not yet worked with that distinction, review Linux Environment Variables Explained and Bash Scripting Basics. Cron brings those concepts together.

⏰ How crontab Syntax Works

A normal user crontab entry has five time-and-date fields followed by a command:

minute0–59
hour0–23
dayday of month
monthmonth
weekdayday of week
Basic form
minute hour day-of-month month day-of-week command

An asterisk means every valid value in that field. Numbers select exact values. Ranges, lists, and step expressions can select several values.

0 3 * * * At 03:00 every day.
*/15 * * * * At minute 0, 15, 30, and 45 of every hour.
30 4 1 * * At 04:30 on the first day of each month.
0 2 * * 1-5 At 02:00 on Monday through Friday.
💡 Steps operate inside a field

A step expression does not mean “wait this long since the previous run.” For example, a minute field such as */15 selects matching minute values inside each hour.

This distinction becomes important with unusual intervals. A schedule such as */35 in the minute field does not create a continuously repeating 35-minute timer; it selects minute values within each hour according to the cron implementation’s field rules.

⚠️ The Day-of-Month and Day-of-Week Trap

The two day fields produce one of cron’s least intuitive behaviors.

With common cron semantics, if both the day of month and day of week fields are restricted, a matching value in either field can make the date eligible.

Potentially surprising schedule
0 9 1 * 1 /home/alex/bin/report.sh

It is tempting to read this as “09:00 on the first Monday of each month.” That is not what the expression means in traditional cron syntax. It can match the first day of a month and Mondays.

When the intended rule is conceptually complex—“the first Monday,” “every 37 hours,” “only after another service succeeds”—a tiny calendar expression may no longer be the clearest or safest representation. Put explicit logic in a well-tested script or consider a scheduler whose model fits the requirement better.

👤 Which User Runs a Cron Job?

A user’s crontab belongs to that user. Commands stored there run with that user’s identity and therefore encounter that user’s normal file permissions and access restrictions.

List your current user’s installed crontab with:

crontab -l

Edit it with:

crontab -e

The exact editor and implementation behavior can vary between systems. More importantly, a user crontab and a system crontab do not necessarily use the same number of fields.

Location / type Typical job format Who runs it?
User crontab managed with crontab Five time fields + command The user who owns the crontab
/etc/crontab on common Linux cron implementations Five time fields + username + command The explicitly named user
Files under /etc/cron.d/ on common implementations Usually system-crontab format The explicitly named user
⚠️ Do not copy formats blindly

A line copied from /etc/crontab into crontab -e can be wrong because the system form may contain an additional username field. Check the manual supplied by the cron implementation installed on your distribution.

🌱 Why Cron Has a Different Environment

Cron does not recreate everything that happened when you opened an interactive terminal. Scheduled commands receive an execution environment appropriate to the scheduler and its implementation.

POSIX defines a baseline that includes variables such as HOME, LOGNAME, PATH, and SHELL. Cron implementations can add behavior and extensions. Your shell startup files, aliases, interactive functions, language-version managers, and manually exported terminal variables should not be assumed to exist.

That is why this can work interactively:

backup-tool --sync

while the same text fails under cron because backup-tool is located in a directory that cron’s PATH does not search.

For scheduled administration, make important dependencies explicit:

PATH=/usr/local/bin:/usr/bin:/bin

15 2 * * * /home/alex/bin/backup.sh

Another strong approach is to keep complex logic inside a script rather than building a long shell program directly inside the crontab. The script can declare its interpreter, validate inputs, use explicit paths where appropriate, and produce meaningful exit statuses.

📁 Relative Paths Are a Reliability Problem

Suppose a script contains:

cp report.csv archive/report.csv

Both paths are relative. Their meaning depends on the process’s current working directory.

An interactive test from one directory therefore does not prove that the scheduled execution will find the same files. A more predictable script identifies the directories it needs explicitly.

cp /home/alex/reports/report.csv /home/alex/reports/archive/report.csv

Absolute paths are not required in every well-designed program, but hidden assumptions about the working directory are especially dangerous in unattended jobs.

📝 Where Does Cron Output Go?

A background job needs observable results. Otherwise “cron did not run” can actually mean “the command ran and failed, but nobody captured the error.”

Traditional cron systems can deliver unredirected standard output and standard error through local mail mechanisms. Minimal servers do not always have a useful mail setup, so do not assume that unattended output will automatically reach you.

One simple approach is to redirect both streams to a file whose location and retention you have deliberately chosen:

15 2 * * * /home/alex/bin/backup.sh >> /home/alex/logs/backup.log 2>&1

The directory must already exist and the cron user must have permission to write there.

For services and production automation, application logging, monitoring, or a scheduler integrated with the system journal may provide stronger observability than an indefinitely growing text file.

🔍 How to Troubleshoot a Cron Job

Troubleshooting is easier when you separate the scheduler from the command.

Question Evidence to collect
Is the schedule installed? Inspect the relevant crontab rather than relying on memory.
Does the command itself work? Run the exact script or executable manually with safe test inputs.
Is the user correct? Confirm which crontab owns the entry and which files that identity can access.
Is a command missing? Inspect PATH assumptions and executable locations.
Did the command report an error? Capture standard output and standard error or inspect the application’s logs.
Did cron itself reject or skip something? Inspect cron’s service or system logs using the tools appropriate to the distribution.

On systemd-based machines, the cron daemon may itself run as a service. Its unit name differs between distributions—for example, you may encounter cron or crond. Identify the actual service before filtering its journal.

This follows the same evidence-first approach used in journalctl Explained: first determine what happened, then change the system.

🧪 Mini Lab: Test a Cron-Like Execution Context Safely

🧪 Goal

Build a tiny script, run it with a deliberately minimal environment, and create a candidate crontab file without installing it. This demonstrates why unattended execution should not depend on your interactive terminal.

1
Create a temporary workspace.
sa_lab_dir=$(mktemp -d)
printf 'Lab directory: %s\n' "$sa_lab_dir"
2
Create a script that reports its context.
cat > "$sa_lab_dir/check-context.sh" <<'EOF'
#!/bin/sh
printf 'time=%s\n' "$(date -Is)"
printf 'home=%s\n' "$HOME"
printf 'path=%s\n' "$PATH"
printf 'shell=%s\n' "${SHELL:-unset}"
printf 'date-command=%s\n' "$(command -v date)"
EOF

chmod u+x "$sa_lab_dir/check-context.sh"
3
Run it with only a small explicit environment.
env -i \
  HOME="$HOME" \
  PATH=/usr/bin:/bin \
  SHELL=/bin/sh \
  "$sa_lab_dir/check-context.sh"

The point is not to reproduce every cron implementation exactly. It is to prove that the script can run without relying on unrelated variables and shell customizations from your terminal.

4
Create a candidate schedule without installing it.
cat > "$sa_lab_dir/candidate.crontab" <<EOF
PATH=/usr/bin:/bin
*/10 * * * * $sa_lab_dir/check-context.sh >> $sa_lab_dir/cron.log 2>&1
EOF

cat "$sa_lab_dir/candidate.crontab"

Nothing has been submitted to cron. You are reviewing the exact text first, which is a useful habit for real automation.

5
Inspect and remove the temporary workspace when finished.
printf 'Removing lab directory: %s\n' "$sa_lab_dir"
ls -ld "$sa_lab_dir"
rm -r -- "$sa_lab_dir"

Confirm that the printed path is the temporary directory created in step 1 before running the removal command.

🔁 What If a Job Is Still Running When the Next One Starts?

A cron schedule describes launch times. It does not automatically prove that the previous invocation has finished.

Imagine a job scheduled every five minutes. Under normal conditions it takes thirty seconds. One day an external service becomes slow and the job takes twelve minutes. New scheduled launches can overlap unless the program or surrounding automation prevents that situation.

Overlapping execution can be harmless for some tasks and dangerous for others. Backups, imports, cleanup processes, billing operations, and jobs that modify shared data should have an explicit concurrency strategy. That may be application-level locking, a system locking utility where appropriate, or a scheduler with stronger execution controls.

🌙 What Happens If the Server Is Off?

A plain calendar-based cron entry should not be treated as a guaranteed catch-up queue. If the scheduler is not running when a scheduled moment occurs, you need to know how your particular implementation and surrounding tooling handle that situation.

Linux systems may use tools such as anacron for jobs that should eventually run even when a machine was unavailable at the original time. systemd timers can also provide catch-up behavior for calendar timers when configured appropriately.

The requirement should come first: “run at exactly 03:00,” “run roughly once per day,” and “run once after the machine returns” are different scheduling requirements.

🆚 Cron or a systemd Timer?

Cron remains a good fit for many small recurring jobs. On systems using systemd, timers provide another scheduling model that integrates with service units.

Need Cron systemd timer
Simple recurring calendar command Very compact More configuration
User-specific periodic task Natural fit with user crontabs Possible with user units
Service dependencies Usually handled outside the cron expression Integrates with the systemd unit model
Journal-based observability Depends on command and system logging setup Integrates naturally with service-unit logging
Configured catch-up after downtime May require additional tooling or implementation features Calendar timers can use persistence when configured

You do not need to replace every cron job simply because systemd exists. Choose the simpler model that still expresses the task reliably.

For the systemd side of this model, continue with What Is systemd? Units, Targets, and the Linux Boot Process Explained .

⚠️ Common Cron Mistakes

❌ “It works in my terminal, so cron should run it.”
Your terminal environment is part of the test. Cron may launch the command with different variables, paths, shell behavior, and working context.
❌ Depending on an implicit PATH.
A command can exist on the server yet remain invisible to the scheduled process. Define the required search path or use deliberate executable paths.
❌ Assuming day-of-month and day-of-week mean AND.
Traditional cron semantics can match either day field when both are restricted. Test the calendar rule rather than reading it like natural language.
❌ Using long shell programs directly in crontab.
Complex quoting, redirection, percent characters, and conditionals become difficult to review. A small tested script is often clearer.
❌ Ignoring standard error.
A command can execute on schedule and fail every time. Preserve useful output somewhere you actually inspect.
❌ Assuming cron prevents overlapping runs.
The next matching schedule can arrive while previous work is still active. Design concurrency behavior deliberately.
❌ Assuming missed calendar events always run later.
Catch-up behavior depends on the scheduler and configuration. Define whether your workload needs exact-time execution or eventual execution.

✅ Knowledge Check

1. What does */15 * * * * select?
It selects every fifteenth minute value inside every hour: commonly minutes 0, 15, 30, and 45.
2. Why can a command work manually but fail under cron?
The scheduled process may receive a different environment, PATH, working context, shell, permissions, or other launch conditions from your interactive terminal.
3. Who runs a command in a normal user crontab?
The command runs under the identity of the user who owns that crontab.
4. Does 0 9 1 * 1 mean only the first Monday of each month?
No. Under traditional cron matching rules, restricting both day-of-month and day-of-week can make either field produce a matching day.
5. Why is redirecting output useful?
It gives you evidence that the command ran and shows messages or errors that would otherwise be easy to miss.
6. Does cron automatically stop a second run when the first is still active?
Do not assume so. If overlapping execution would be unsafe, the job needs an explicit concurrency-control strategy.

🎓 Servers Academy — Key Takeaway

Think beyond the five asterisks.

A reliable cron job has four parts: a calendar rule, an execution identity, an environment, and a command whose result you can observe.

When a scheduled task fails, inspect those layers in that order. Confirm the schedule, confirm the user, remove hidden environment assumptions, and capture the command’s output. That turns cron troubleshooting from guessing into a repeatable investigation.

📚 Official References

Cron implementations and distribution defaults can differ. When behavior such as daemon naming, extensions, timezone handling, logging, or system crontab layout matters, check the manuals installed on the target server.

Rate article
Servers Academy
Add a comment