Skip to content
Blazej Mrozinski
PL

Cron

Infrastructure

Cron, defined

Cron is the time-based job scheduler built into Unix-like operating systems, used to run scripts or commands on a recurring schedule defined in a crontab file.

Cron

Cron is the oldest scheduler on Linux and still the right tool for most recurring work on a server. It runs in the background, reads a simple schedule file, and executes commands when their time comes up. Backups, SSL certificate renewals, cache warming, log rotation, database cleanups, healthchecks: nearly every automated maintenance job on a VPS is a cron job. The syntax is blunt but stable. The same crontab that worked in 1995 works today.

How Cron Works

The cron daemon (cron on Debian-family systems, crond on RHEL-family) runs continuously and checks every minute whether any configured job is due. Jobs live in crontab files, which come in three main flavors:

  • User crontabs, edited via crontab -e. Each user has their own, and jobs run as that user. The root crontab runs as root, which suits system tasks and is dangerous for everything else.
  • System cron directories like /etc/cron.hourly/, /etc/cron.daily/, /etc/cron.weekly/, and /etc/cron.monthly/. Drop an executable script in any of these and it runs on that cadence, as root, without a time field.
  • /etc/cron.d/, which holds crontab-format files that also specify the user to run as. Packages often install their own cron definitions here (e.g. certbot for Let’s Encrypt renewal).

Which one to use is a question of ownership. For application-level jobs (WordPress maintenance, per-site scripts), put them in the application user’s crontab. For system-level tasks (certbot, fail2ban, unattended-upgrades), leave them wherever the package installed them.

Crontab Syntax

The schedule format is five space-separated fields followed by the command:

* * * * * /path/to/command
│ │ │ │ │
│ │ │ │ └── day of week (0-6, 0 = Sunday)
│ │ │ └──── month (1-12)
│ │ └────── day of month (1-31)
│ └──────── hour (0-23)
└────────── minute (0-59)

A real example, running a backup every day at 3:00 AM:

0 3 * * * /usr/local/bin/backup.sh

Each field accepts lists (1,15), ranges (1-5), and step values (*/5 = every five minutes). Getting the fields wrong is the most common cron mistake; tools like crontab.guru exist specifically to sanity-check schedules before saving them.

Where Output Goes

By default, anything a cron job writes to stdout or stderr is mailed to the user running it. On a modern VPS without a mail server, that means it disappears. Two things to configure:

  1. Set MAILTO="" at the top of the crontab to silence attempted mail delivery, or MAILTO="[email protected]" if you actually have mail working.
  2. Redirect output to a log file per job: >> /var/log/my-job.log 2>&1. Then add the file to /etc/logrotate.d/ so it gets rotated.

Silent cron jobs that fail silently are the classic production footgun. Logging the output and monitoring the log (or the absence of expected output) is what turns a cron job into dependable automation.

WP-Cron vs System Cron

WordPress has its own scheduler called WP-Cron, which fires on page load instead of running on a real timer. It works fine on small sites with steady traffic, but it has two production problems: jobs only run when someone visits the site, and every page load pays a small cost checking the cron queue, which wastes PHP-FPM workers under load.

The production pattern is to disable WP-Cron and drive it from system cron instead. In wp-config.php:

define('DISABLE_WP_CRON', true);

Then in the system crontab, every five minutes:

*/5 * * * * cd /var/www/example.com && wp cron event run --due-now >/dev/null 2>&1

Now WordPress’s scheduled hooks fire on a reliable cadence and the cost is paid once per interval instead of on every request.

Typical Jobs on a WordPress Server

A working server crontab covers a handful of recurring tasks, all driven by cron:

  • certbot renew (twice daily, per Let’s Encrypt’s guidance), installed automatically by certbot’s package
  • Database and file backups to object storage: a custom script, usually nightly
  • Cache warming after deploys or on a regular schedule
  • Transient cleanup via WP-CLI, daily or weekly
  • Healthchecks via watchdog monitoring, every five minutes
  • Log rotation via logrotate’s own cron entry, usually daily

Everything above is a one-line crontab entry calling a small shell script. Once it’s in place, you stop touching the server for routine maintenance, which is exactly the point.

See also