# Getting Started with Cron Jobs on Linux

Cron is a time-based job scheduler for Unix-like operating systems. It is used
to automate various tasks, typically system maintenance and administration
related jobs such as:

- Backing up databases, files, and directories.
- Creating and sending scheduled reports for certain activities or events.
- Verifying if a service is accessible.

Cron allows these tasks to be executed periodically at selected times, dates,
months, and intervals. The `cron` command utility can be found on all Unix-like
systems, most commonly on macOS and Linux-based systems.

This article will explain the Cron syntax and show you how to create and edit
cron jobs on a Linux-based machine. You'll also learn how to [monitor cron jobs](https://betterstack.com/community/guides/monitoring/what-is-cron-monitoring/)
so that if a job fails to run, you will be notified so that you can take the
appropriate action to fix the problem.

[ad-uptime-small]

## Prerequisites

Before continuing with the sections below, ensure that you have a Linux-based
machine at hand. This could be a PC, virtual machine, virtual private server, or
[WSL](https://docs.microsoft.com/en-us/windows/wsl/) (if you are using Windows).
You also need root access so ensure to login to your machine using a non-root
user with administrative privileges.

We'll be demonstrating the concepts discussed in this article on an Ubuntu 22.04
server, but the steps should be pretty similar on most mainstream Linux
distributions.

## Step 1 — Getting started with Cron

The `cron` command utility comes preinstalled on every Ubuntu installation, so
you can check its status using the following command:

```command
sudo systemctl status cron
```

```text
[output]
● cron.service - Regular background program processing daemon
 Loaded: loaded (/lib/systemd/system/cron.service; enabled; vendor preset: >
 [highlight]
 Active: active (running) since Wed 2022-08-31 16:23:02 UTC; 4min 9s ago
 [/highlight]
 Docs: man:cron(8)
 Main PID: 751 (cron)
 Tasks: 1 (limit: 2258)
 Memory: 408.0K
 CPU: 3ms
 CGroup: /system.slice/cron.service
 └─751 /usr/sbin/cron -f -P
```

If you're getting the same output above, it means `cron` is up and running on
your system, and you can skip ahead to the next section. However, just in case
the service is not found or not installed on your machine, we will describe how
to install and set it up in this section.

First, update your system repositories to the latest version using the following
command:

```command
sudo apt update -y
```

After the updates are completed, you can install Cron on your system:

```command
sudo apt install cron -y
```

Next, you'll need to enable the service and start it so that it is launched in
the background and keeps running even if the server is rebooted:

```command
sudo systemctl enable cron
```

```text
[output]
Synchronizing state of cron.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable cron
```

```command
sudo systemctl start cron
```

Check the status of `cron` again:

```command
sudo systemctl status cron
```

At this point, you should observe that the `cron` service is now up and running.

## Step 2 — Writing your first Cron job

In this section, we will discuss how to automate a simple but repetitive task
using Cron. All Cron jobs are managed through a special program called
`crontab`, and every operating system user has their own `crontab` file which is
located in the `/var/spool/cron/crontabs/` directory.

You can open up your `crontab` file using the following command:

```command
crontab -e
```

If you are running this command for the first time, you will get the following
output asking you to select a text editor:

```text
[output]
no crontab for eric - using an empty one

Select an editor. To change later, run 'select-editor'.
 1. /bin/nano <---- easiest
 2. /usr/bin/vim.basic
 3. /usr/bin/vim.tiny
 4. /bin/ed

Choose 1-4 [1]:
```

Select `/bin/nano` and press `Enter`. Your default crontab editor will be set to
Nano and a temporary `crontab` file will be created and opened in the editor.
This file contains some text explaining a Cron expression's basic syntax. Note
that any line beginning with the `#` character is signifies a comment.

```text
[label crontab]
# Edit this file to introduce tasks to be run by cron.
. . .
[highlight]
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
[/highlight]
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h dom mon dow command
```

### Basic Cron syntax

A Cron expression has the following structure:

```text
minute hour day_of_month month day_of_week command
```

Each of the above fields takes the following values:

- `minute`: `0-59`
- `hour`: `0-23`
- `day_of_month`: `1-31`
- `month`: `1-12` or `JAN-DEC`
- `day_of_week`: `0-6` or `SUN-SAT`
- `command`: Any operating system commands. When you need to execute multiple
  commands in one Cron Job, you can chain them together using `&&`.

For example, the expression below means the commands
`curl https://betterstack.com` and `curl https://www.google.com` will be
executed every Sunday at 04:05 AM. The asterisk (`*`) is a wildcard variable,
meaning that all possible values are accepted. Cron will treat the first five
parameters, separated by spaces, as scheduling parameters, and the rest will be
treated as Linux commands.

```text
. . .
5 4 * * sun curl https://betterstack.com && curl https://www.google.com
```

Lastly, press `CTRL + X` to exit the editor, and then press `Y` to save the
file, and it will be saved as `/var/spool/cron/crontabs/<username>`. You can
open this file again using the same command (`crontab -e`). Note that
`crontab -e` opens a file in the `/tmp` directory instead of the actual crontab
file so that it can check your modifications for errors and prevent you from
overwriting your actual crontab file with those errors.

Let's now look at how to define more complex schedules with cron. For example,
you might want the job to repeat every Monday, Tuesday, and Saturday at 00:00.
To define schedules like this, we need a more powerful syntax, as shown in the
list below:

- `*`: This is a wildcard variable representing "all". For instance, `* * * * *`
  means this Cron Job will be repeated every minute of every hour of every day.
- `,`: Commas are used to break up values to form a list. For instance,
  `0 0 * * mon,tue,sat` specifies a job which repeats at 00:00 every Monday,
  Tuesday, and Saturday.
- `-`: A hyphen is used to define a range of values. For example, `0-9 * * * *`
  means this job will be repeated for the first 10 minutes of every hour of
  every day.
- `/`: A forward slash is used to define a step interval. For example,
  `*/5 * * * *` defines a job that will be repeated every 5 minutes.

To make sure you are familiar with these syntaxes, here are some more examples:

- `5 7 3 8 *`: Every August 3rd at 7:05 AM.
- `23 0-20/2 * * *`: At minute 23 for every second hour from 0 to 20. (00:23,
  02:23, 04:23, and so on)
- `0 0,12 1 */2 *`: At 00:00 and 12:00 for the first day of every second month.
  (February 1st, April 1st, June 1st ...)

If you're still having trouble understanding these expressions, there is a handy
website called [Crontab guru](https://crontab.guru) that allows you to test a
Cron expression before using it in your `crontab`. Besides these special
syntaxes, Cron also tries to simplify your workflow by introducing these
shortcuts.

- `@reboot`: the scheduled task will run once at system startup.
- `@yearly` or `@annually`: run once a year, same as `0 0 1 1 *`.
- `@monthly`: run once a month, same as `0 0 1 * *`.
- `@weekly`: run once a week, same as `0 0 * * 0`.
- `@daily` or `@midnight`: run once a day, same as `0 0 * * *`.
- `@hourly`: Run once every hour, same as `0 * * * *`.

You can replace the schedule variables using these shortcuts like this:

```text
@yearly curl https://google.com
```

The above Cron expression is the same as:

```text
0 0 1 1 * curl https://google.com
```

## Step 3 — Managing `crontab` files

Besides the `crontab -e` command discussed earlier, which is used to open the
corresponding `crontab` file for the current user, there are some other
available commands for `crontab`:

- `crontab -l`: echos the current contents of the `crontab` file to the console.
- `crontab -r`: Remove the current `crontab` file. You should not use this
  command for safety reasons because it won't ask you to confirm the deletion of
  the `crontab` file.
- `crontab -r -i`: This is the recommended command to use when you wish to
  delete a `crontab` file, as it will ask you to confirm your choice like this:

```command
crontab -r -i
```

```text
[output]
crontab: really delete eric's crontab? (y/n)
```

- `crontab -e`: This command opens up your `crontab` file for editing.
- `sudo crontab -u <user> -e`: If a user has been granted root privileges, that
  user will be able to edit another user's `crontab` file using this command.

If you manage an operating system with multiple users, you may create prevent
certain users from accessing these `crontab` commands for security reasons.

This can be done by specifying usernames in a `/etc/cron.deny` file or a
`/etc/cron.allow` file. The former prevents users in the file from accessing the
crontab commands while the latter allows only the users in the file to access
the commands. If both files are present, then `cron.allow` will override
`cron.deny`, meaning that a user present in both files will be granted access to
the `crontab` command.

For instance, if you want to deny access to all users but grant access to only a
specific user, you can use the following commands:

```command
sudo echo ALL >>/etc/cron.deny # deny access to all users
```

```command
sudo echo jack >>/etc/cron.allow # grant access to the user `jack`:
```

## Step 4 — Executing script files with Cron

In this section, we'll automate a sequence of commands in order using a shell
script. This script will retrieve system usage information such as CPU, memory
and disk usage, as well as internet speed, and save them to a file.

Go ahead and create a new directory in your home directory with the following
command:

```command
cd ~ && mkdir scripts
```

Create a new script file within the `scripts` directory, and populate it with
the following contents:

```command
nano scripts/system-usage.sh
```

```bash
[label ~/scripts/system-usage.sh]
#! /bin/bash

: > ~/system-usage.txt

echo "============================== CPU and Memory Usage ==============================" >> ~/system-usage.txt

top -b -n 1 >> ~/system-usage.txt

echo "================================== Disk Usage ====================================" >> ~/system-usage.txt

df -h >> ~/system-usage.txt

echo "============================= Internet Speed Test ================================" >> ~/system-usage.txt

speedtest >> ~/system-usage.txt
```

Before running the script, ensure to install the `speedtest-cli` package with
the following command:

```command
sudo apt install speedtest-cli
```

Next, make the script executable by the current user:

```command
chmod +x scripts/system-usage.sh
```

Test the script to confirm that it's working as expected:

```command
./scripts/system-usage.sh
```

Once the script exits, check the contents of the `system-usage.txt` file in the
home directory:

```command
cat ~/system-usage.txt
```

You should observe the following output:

```text
[output]
============================== CPU and Memory Usage ==============================
top - 18:18:28 up 1 day, 1:55, 1 user, load average: 0.00, 0.00, 0.00
Tasks: 130 total, 1 running, 129 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 3.2 sy, 0.0 ni, 96.8 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 1927.7 total, 1289.0 free, 152.1 used, 486.6 buff/cache
MiB Swap: 0.0 total, 0.0 free, 0.0 used. 1612.4 avail Mem
. . .
================================== Disk Usage ====================================
Filesystem Size Used Avail Use% Mounted on
tmpfs 193M 1.1M 192M 1% /run
/dev/sda1 38G 2.0G 34G 6% /
tmpfs 964M 0 964M 0% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
/dev/sda15 253M 1.1M 252M 1% /boot/efi
tmpfs 193M 0 193M 0% /run/user/1000
============================= Internet Speed Test ================================
Retrieving speedtest.net configuration...
Testing from Hetzner Online GmbH (5.161.145.150)...
Retrieving speedtest.net server list...
Selecting best server based on ping...
Hosted by All Points Broadband (Ashburn, VA) [1774.19 km]: 1.337 ms
Testing download speed................................................................................
Download: 1328.87 Mbit/s
Testing upload speed......................................................................................................
Upload: 910.45 Mbit/s
```

Now, create a Cron job that runs this Bash script periodically.

```command
crontab -e
```

Add the following line to the end of the file and quit your editor afterward:

```text
*/5 * * * * bash ~/scripts/system-usage.sh
```

With this configuration in place, your system usage information will be
retrieved every 5 minutes, and the output will be saved to the
`~/system-usage.txt` file as demonstrated earlier.

## Step 5 — Monitoring Cron output

Since the Cron Jobs are executed in the background, you will not receive
feedback when the task executes so you must actively monitor each Cron Job to
get notified when it succeeds or fails. Typically, there are two major ways to
[monitor your cron jobs](https://betterstack.com/community/comparisons/cronjob-monitoring-tools/) and both will be demonstrated in this section.

### 1. Monitoring cron jobs via email

By default, `cron` will attempt to send an email if your scheduled task produces
some output such as the one shown below:

```text
* * * * * <command> && echo "Cron Job executed."
```

However, for this to work, you need to set up an SMTP server on your machine. In
this section, we'll talk about how to send emails using
[sSMTP](https://wiki.archlinux.org/title/SSMTP) and Gmail. Start by installing
`ssmtp` on your computer:

```command
sudo apt update
```

```command
sudo apt install ssmtp
```

Next, open its configuration file:

```command
sudo nano /etc/ssmtp/ssmtp.conf
```

Update the contents of the file as follows:

```text
[label /etc/ssmtp/ssmtp.conf]
#
# Config file for sSMTP sendmail
#
# The person who gets all mail for userids < 1000
# Make this empty to disable rewriting.
[highlight]
root=<your_email_address>
[/highlight]

# The place where the mail goes. The actual machine name is required no
# MX records are consulted. Commonly mailhosts are named mail.domain.com
mailhub=smtp.gmail.com:465

# Where will the mail seem to come from?
rewriteDomain=gmail.com

# The full hostname
[highlight]
hostname=<your_hostname>.<your_ip>.tld
[/highlight]

# Use implicit TLS (port 465). When using port 587, change UseSTARTTLS=Yes
TLS_CA_FILE=/etc/ssl/certs/ca-certificates.crt
UseTLS=Yes
UseSTARTTLS=No

# Username/Password
[highlight]
AuthUser=<your_email_address>
AuthPass=<password>
[/highlight]
AuthMethod=LOGIN

# Are users allowed to set their own From: address?
# YES - Allow the user to specify their own From: address
# NO - Use the system generated From: address
FromLineOverride=YES
```

One thing to note is that you must enable 2-Step-Verification for your Google
account and then generate a unique
[App Password](https://myaccount.google.com/apppasswords) to use in
`ssmtp.conf`, you cannot use your Google password directly.

![Google App Passwords](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/a696e704-8a0e-4e26-dd5c-957cc0da2900/public =897x768)

Next, open your crontab file again, and this time, you can output the content of
the `system-usage.txt` file:

```command
crontab -e
```

```text
[label crontab]
. . .
MAILTO="<your_email_address>"

*/5 * * * * bash ~/scripts/system-usage.sh && echo "$(cat ~/system-usage.txt)"
```

Remember to add the `MAILTO` option shown above, otherwise, the cron emails will
be sent to `<username>@gmail.com`. This particular job will run every five
minutes, and the output will be sent to the email address specified by `MAILTO`.
Wait for five minutes, and you should receive the following email in your inbox:

![Cron Email](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/a3ae21f5-fc99-4171-20b0-be6b032fa600/public =808x768)

### 2. Monitoring Cron Jobs with a cloud service

Another way to monitor Cron Jobs is by use a cloud service that provides Cron
monitoring capabilities. This is typically the way to go for serious production
applications where the sustained failure of a scheduled task can potentially
cause a massive problem for the business.

[Better Uptime](https://betterstack.com/better-uptime) is a monitoring tool that
offers [Cron Job monitoring services](https://betterstack.com/community/comparisons/cronjob-monitoring-tools/). It provides much more flexibility for
alerting options and keeps track of the health of the service over time. You can
set up the exact person or team that should be alerted when a job fails and even
create status pages for communicating downtime to your customers.

In this section, you will use Better Uptime to monitor the system usage cron job
that you set up earlier. First, you need to
[create a free Better Uptime account](https://betteruptime.com/users/sign-up) if
you don't have one already. Once signed in, click **Heartbeats** on the
navigation menu and create your first heartbeat.

![Better Uptime Heartbeats](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/e7d16a14-8c62-465e-5167-d5823a9d3900/public =1303x768)

Enter an appropriate name for your monitor and how often you expect the
monitored task to be repeated (five minutes in this case). Do enter an
appropriate grace period to account for slight delays due to the network. In the
**On-call escalation** section, select how you wish to be notified when an
incident is detected. Once you are done, click the **Save Changes** button.

![Better Uptime create heartbeat](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/9e271f84-befa-4181-e4b2-4e88b8220000/public =1032x768)

You will see this page next:

![Better Uptime heartbeat URL](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/b4be7c09-467f-47cf-6069-5d205ec6ff00/public =1005x768)

The highlighted URL above is what Better Uptime uses to monitor your Cron
activity. You need to set up the job such that once it executes, a HEAD, GET, or
POST request is made to this URL. Once Better Uptime receives the request, the
job will be recorded as successfully executed.

Suppose the request is not received for any reason within the specified time
period (including the grace period). An incident will be recorded and you will
be notified through the appropriate channel.

Head back to the terminal and open your `crontab` file once again:

```command
crontab -e
```

Edit the cron job that you wish to monitor:

```text
[label crontab]
. . .
*/5 * * * * bash ~/scripts/system-usage.sh && echo "$(cat ~/system-usage.txt)" && curl https://betteruptime.com/api/v1/heartbeat/<your-heartbeat-monitor-id>
```

Save this crontab by exiting your editor, then wait for the job to run. You
should observe that your monitor is marked as "Up" meaning that the job is
running as scheduled.

![Heartbeat Running](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/73b3efa3-578a-496a-62bd-e1ec50749300/public =1366x591)

You can also make a request to the Better Uptime URL in the bash script like
this:

```bash
[label ~/scripts/system-usage.sh]
. . .

curl https://betteruptime.com/api/v1/heartbeat/<your-heartbeat-monitor-id>
```

This way, when you have multiple scripts in one job, you can set up a heartbeat
for each script, and you'll be able to tell which one is not working when an
incident happens.

If you want to simulate an incident, stop the `cron` service with the command
below:

```command
sudo systemctl stop cron
```

Better Uptime will stop receiving the requests and mark your monitor as "Down":

![Heartbeat Incident](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/a690593d-c3c0-404d-cef8-fbed13e04f00/public =1366x587)

You will also get an alert in the configured channels:

![Better Uptime Incident Email Alert](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/f0e9ce9c-8355-4ca5-fb95-6252339ff300/public =794x668)

You can view details about the incident or acknowledge it, indicating that you
are working to resolve it, or you can escalate the incident further by alerting
one or more members of the relevant team, or even the entire organization.

## Final thoughts

In this tutorial, we discussed how to use the `cron` utility to automate tasks
on Linux and how to monitor cron jobs through email or a cloud monitoring
service like Better Uptime.

You should now understand the Cron scheduling syntax and how to create and
monitor any cron jobs. To learn more about the `cron` command, see its
[manual page](https://www.man7.org/linux/man-pages/man5/crontab.5.html)
(`man cron`), or view checkout
[these articles](https://betterstack.com/community/questions/?q=cron) tackling
common questions about the program.
