Skip to content

crontab and System cron

cron Basics covered the five-field time syntax of a crontab line. The next question is more practical: how do you actually install such a line, where can you inspect or remove it, and how does an ordinary user's crontab differ from cron running at the system level? Missing this distinction is one of the most common sources of confusion in practice — a job appears to be "not in cron," when in fact it was written under a different user's account entirely.

What You Will Learn

By the end of this article you will be able to:

  • use the crontab command's -e, -l, -r, and -u flags;
  • explain where the user crontab is stored, and why editing it directly is discouraged;
  • distinguish /etc/crontab and /etc/cron.d/ — system-level crontabs — from a per-user crontab;
  • explain the /etc/cron.daily/-style directories and how run-parts executes them;
  • control who is allowed to use cron at all with cron.allow and cron.deny.

The User Crontab: the crontab Command

Each user manages their own crontab through the crontab command rather than by opening a file directly, because the command notifies the cron daemon that the schedule changed as soon as it saves.

crontab -e

This opens the editor named in the $EDITOR (or $VISUAL) environment variable. If neither is set, the system typically falls back to nano or offers a selection menu:

echo $EDITOR

export EDITOR=nano
crontab -e

Add the following line and save:

0 7 * * 1 /home/<username>/scripts/weekly-report.sh

Verify:

crontab -l
0 7 * * 1 /home/<username>/scripts/weekly-report.sh

crontab -l prints the current user's active crontab to the screen; there's no need to know where the file lives on disk, because it's never opened directly.

Where the User Crontab Lives

sudo ls -l /var/spool/cron/crontabs/
-rw------- 1 <username> crontab 68 Jul 26 09:15 <username>

Each user gets a separate file, with 600 permissions — readable only by its owner (and root). Editing this file directly is discouraged: crontab -e validates the file's syntax before saving, while editing the file directly bypasses that check entirely, and a malformed crontab can be silently ignored by the daemon.

Managing Another User's Crontab

sudo crontab -u www-data -l

The -u flag lets an administrator, via sudo, view or edit another user's crontab. This is common for service accounts (www-data, postgres, and similar) that can't log in interactively themselves.

Clearing a Crontab

crontab -r

-r removes all of the current user's cron jobs, immediately, without confirmation.

crontab -r is not reversible

There is no selective removal — the entire file disappears at once. To remove a single job, open crontab -e and delete that line by hand, or comment it out with a leading # to disable it temporarily. Before running -r, especially on a production server, save the current state first: crontab -l > ~/crontab-backup-$(date +%F).txt.

System-Level cron: /etc/crontab

Alongside per-user crontabs, there is a single shared schedule file for the whole system:

cat /etc/crontab
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d.

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

17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
25 6    * * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )

The key difference is visible immediately — there is a sixth column. In a user crontab, the command follows the time fields directly; in /etc/crontab, the time fields are followed by which user the command runs as (root in the example above), and only then the command itself.

Property User crontab (crontab -e) /etc/crontab
Who edits it Each user, their own Root only
Number of columns 5 time fields + command 5 time fields + user + command
Runs as which user Always the owner Whichever user the line names
How it's installed crontab -e reloads it automatically Saving the file is enough, no separate command
Typical use Personal or project-specific jobs System-level jobs, often installed by packages

/etc/cron.d/: Separate Files for Packages

Rather than editing /etc/crontab directly, a separate file is usually added to /etc/cron.d/ — this lets a package install or upgrade its own schedule without touching a shared file:

sudo nano /etc/cron.d/backup-check
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

30 3 * * * root /usr/local/bin/backup-check.sh

Files in /etc/cron.d/ use the same six-column format as /etc/crontab, but they don't go through crontab -e — creating or editing the file directly is enough, since the cron daemon watches this directory continuously.

/etc/cron.daily/ and Similar Directories

Rather than writing an exact time, many system scripts (logrotate, and the package manager's automatic cleanup tasks, for example) are placed into ready-made directories instead:

ls /etc/cron.daily/ /etc/cron.weekly/ /etc/cron.monthly/
/etc/cron.daily/:
apt-compat  dpkg  logrotate

/etc/cron.weekly/:
man-db

/etc/cron.monthly/:
0anacron

Every file in these directories is an executable script. The mechanism that actually runs them at the right time — visible in the /etc/crontab example above — is run-parts:

run-parts --test /etc/cron.daily

run-parts --test shows which scripts would be run, without actually running them. --report (seen in the /etc/crontab example above) makes it write a short report about each script's result.

These directories are convenient for jobs that only need an approximate time ("once a day, in the morning") rather than an exact minute — dropping a script into /etc/cron.daily/ is simpler than writing a full line in /etc/cron.d/ when precision doesn't matter.

The silent filename trap: no dots, no extension

Both /etc/cron.d/ (read directly by cron) and the run-parts directories (/etc/cron.daily/ and its siblings) ignore any filename containing a . — and more generally any name with characters outside [A-Za-z0-9_-]. A file dropped in as backup-check.sh or backup.check is skipped with no error logged anywhere; the identical file renamed backup-check runs. This is the single most common "my /etc/cron.d/ job just doesn't run and nothing appears in the log" cause — and it is exactly why the drop-in file created above is named backup-check, not backup-check.sh. Scripts placed in the run-parts directories additionally must be executable (chmod +x) and owned by root; a script that is present but not executable is likewise skipped in silence.

anacron

On many modern systems these directories are managed by anacron, which is designed for machines that aren't always powered on (a laptop, for instance) and runs a missed job the next time the system starts up. On a server that is always running, this distinction rarely matters in practice.

Who Is Allowed to Use cron: cron.allow and cron.deny

On a multi-user server, it isn't always desirable for every account to be able to schedule its own jobs. This is controlled by two optional files:

cat /etc/cron.allow 2>/dev/null
cat /etc/cron.deny 2>/dev/null

The logic works like this:

  • if /etc/cron.allow exists — only the users listed in it can use crontab, everyone else is refused;
  • if /etc/cron.allow doesn't exist but /etc/cron.deny does — everyone except the users listed can use it;
  • if neither exists (the default on Ubuntu) — every user can use cron.
sudo nano /etc/cron.deny
guest
tempuser

This is a simple way to keep temporary or guest accounts, for example, from adding automated jobs of their own.

Practical Scenario: A Scheduled Task Under a Service Account

Problem. The application directory owned by www-data needs a cleanup job every morning at 4:00, but www-data can't log in interactively, so crontab -e can't be run as that user directly.

Solution — edit another user's crontab with sudo -u-style access:

sudo crontab -u www-data -e
0 4 * * * find /var/www/myapp/tmp -type f -mtime +1 -delete

Verify:

sudo crontab -u www-data -l
0 4 * * * find /var/www/myapp/tmp -type f -mtime +1 -delete
sudo ls -l /var/spool/cron/crontabs/www-data
-rw------- 1 www-data crontab 58 Jul 26 09:20 www-data

Conclusion. The job works correctly against files owned by www-data because it runs as that exact user — had the command instead been placed in a personal crontab, find ... -delete might well have lacked permission to touch www-data's files.

Common Mistakes

  • Confusing sudo crontab -e with crontab -e. sudo crontab -e edits root's crontab, not the current user's. To edit another user's crontab, name it explicitly with -u (sudo crontab -u <username> -e).
  • Editing /var/spool/cron/crontabs/ directly. That file is managed by the crontab command. Opening it in a text editor directly — even with correct syntax — can leave the cron daemon unaware that the file changed, since the daemon typically expects changes to arrive through the crontab command.
  • Adding a line to /etc/crontab without the user column. If a line in /etc/crontab or /etc/cron.d/ only has the five time fields, as in a user crontab, cron interprets the sixth column — expected to be a username — as part of the command instead, and the job doesn't run as intended.

Exercises

  1. Using crontab -e, add a job under your own user account that runs date >> ~/cron-test.log every day at 6:00. Verify it with crontab -l, then remove it with crontab -e.
  2. List the scripts currently present in /etc/cron.daily/ and compare that list against the output of run-parts --test /etc/cron.daily.
  3. Check whether cron.allow and cron.deny exist on your system, and explain in writing what the default behavior is when neither is present.
  4. For a hypothetical deploy service account, write the command that would open its crontab for editing (without actually running it).

Summary

crontab exists at three levels: a personal per-user crontab (crontab -e), system-level schedules in /etc/crontab and /etc/cron.d/, and ready-made directories like /etc/cron.daily/. Each differs in who edits it, which user it runs as, and what it's typically used for. If a job "isn't in cron," the first thing to check is the simplest one — whose crontab it was actually added to. Next, cron Environment and Logs covers the opposite problem: the job is registered correctly but doesn't behave as expected, because cron's environment differs from an interactive terminal's.

References