# Cron Expressions with Examples: crontab, Kubernetes CronJob, GitHub Actions and EventBridge

> Learn to read and write cron expressions with examples, set up crontab safely with logs and locks, and reuse the syntax in Kubernetes, GitHub Actions and AWS.

- Source: https://www.itwonderlab.com/cron-expressions-examples/
- Published: 2026-09-13
- Updated: 2026-09-13
- Author: Javier Ruiz Jiménez (https://www.javierruizjimenez.com/)
- Site: IT Wonder Lab (https://www.itwonderlab.com/)

---

## What is a cron expression

[Cron](https://www.itwonderlab.com/cron/) runs a command on a schedule. The schedule is a **cron expression** of five fields separated by spaces:

```text
┌───────── minute        (0-59)
│ ┌─────── hour          (0-23)
│ │ ┌───── day of month  (1-31)
│ │ │ ┌─── month         (1-12 or JAN-DEC)
│ │ │ │ ┌─ day of week   (0-7, 0 and 7 are Sunday, or SUN-SAT)
│ │ │ │ │
* * * * *  command
```

A command runs when **all five fields match** the current time.

## Operators

| Symbol | Meaning | Example |
|---|---|---|
| `*` | every value | `* * * * *` every minute |
| `,` | list | `0 8,20 * * *` at 08:00 and 20:00 |
| `-` | range | `0 9 * * 1-5` at 09:00, Monday to Friday |
| `/` | step | `*/15 * * * *` every 15 minutes |

`*/15` means 0, 15, 30 and 45. A step over a range, `8-18/2`, means 8, 10, 12, 14, 16 and 18.

## Examples

| Schedule | Expression |
|---|---|
| Every minute | `* * * * *` |
| Every 5 minutes | `*/5 * * * *` |
| Every hour, at minute 0 | `0 * * * *` |
| Every day at 02:30 | `30 2 * * *` |
| Weekdays at 09:00 | `0 9 * * 1-5` |
| Every 15 minutes, 08:00 to 17:45 | `*/15 8-17 * * *` |
| Sundays at 03:00 | `0 3 * * 0` |
| First day of each month at midnight | `0 0 1 * *` |
| Every quarter (Jan, Apr, Jul, Oct) | `0 0 1 1,4,7,10 *` |
| Twice a day, at 06:00 and 18:00 | `0 6,18 * * *` |
| Every Monday and Thursday at 22:00 | `0 22 * * 1,4` |

Shortcuts available in most crontabs: `@reboot`, `@hourly`, `@daily`, `@weekly`, `@monthly` and `@yearly`.

> [!WARNING]
> If you set **both** the day of the month and the day of the week, most cron implementations run the job when **either** matches. `0 0 13 * 5` runs on every 13th **and** every Friday, not only on Friday the 13th.

## crontab

Each user has a crontab:

```bash
crontab -l       # list
crontab -e       # edit
crontab -r       # remove all (careful: no confirmation)
sudo crontab -u www-data -l
```

System files in `/etc/cron.d/` and `/etc/crontab` have a sixth field with the user: `30 2 * * * root /usr/local/bin/backup.sh`.

## A safe cron job

Cron starts jobs with a tiny environment, a few things go wrong again and again:

```text
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=""

# backup every day at 02:30, only one copy at a time, with a log
30 2 * * * /usr/bin/flock -n /tmp/backup.lock /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
```

- **Use full paths** for programs and files, or set `PATH` at the top.
- **Log the output** (`>> file 2>&1`). Otherwise cron tries to email it, and the error is lost.
- **Avoid overlaps** with `flock -n`, so a slow run does not start a second one.
- **Escape `%`**: in a crontab line a percent sign means a newline. Write `date +\%F`.
- **Check the time zone**: cron uses the server time zone, which is usually UTC in the cloud.
- **Test the script** by hand, with `env -i /bin/sh -c '/usr/local/bin/backup.sh'`, to see what cron sees.

A good example is the backup of the [rsync tutorial](https://www.itwonderlab.com/rsync-tutorial-backup-ssh/). Debug with `grep CRON /var/log/syslog` (Debian and Ubuntu) or `journalctl -u cron`.

## Cron in Kubernetes

A `CronJob` uses the same five fields (in the time zone of the controller, or in the one you set with `timeZone`):

```yaml title="cronjob.yaml"
apiVersion: batch/v1
kind: CronJob
metadata:
  name: cleanup
spec:
  schedule: "0 3 * * *"
  timeZone: "Europe/Madrid"
  concurrencyPolicy: Forbid
  successfulJobsHistoryLimit: 3
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: cleanup
              image: busybox:1.36
              command: ["sh", "-c", "echo cleaning; date"]
```

`concurrencyPolicy: Forbid` skips a run if the previous one is still active. Create it with `kubectl apply -f cronjob.yaml` and run it now with `kubectl create job --from=cronjob/cleanup test-run` (see [kubectl](https://www.itwonderlab.com/kubectl-cheat-sheet/)).

## Cron in GitHub Actions

```yaml
on:
  schedule:
    - cron: "0 3 * * 1"    # Mondays at 03:00 UTC
  workflow_dispatch:
```

The time is always UTC and runs can be delayed in busy periods. See [GitHub Actions](https://www.itwonderlab.com/github-actions/).

## Cron in AWS

[EventBridge](https://www.itwonderlab.com/aws-eventbridge/) schedules have **six** fields (a year field) and use `?` for "no specific value" in the day of the month or the day of the week:

```text
cron(30 2 * * ? *)      # every day at 02:30 UTC
cron(0 9 ? * MON-FRI *) # weekdays at 09:00 UTC
rate(15 minutes)        # fixed rate, no cron needed
```

Defined with Terraform, the schedule is a string in the rule or the scheduler resource. Because AWS adds the year and requires `?`, an expression from a crontab does not copy over unchanged.

## Check an expression

Before deploying, check the next runs with a tool such as `crontab.guru` or a small script:

```python
from datetime import datetime
from croniter import croniter   # pip install croniter

it = croniter("*/15 8-17 * * 1-5", datetime(2026, 10, 5, 7, 50))
for _ in range(4):
    print(it.get_next(datetime))
```

## Cron or systemd timers

systemd timers add logs in the journal, `Persistent=true` to run a missed job after a reboot, and dependencies. Cron is simpler and is the same on every Unix. For jobs in a cluster use a Kubernetes `CronJob`; for serverless use EventBridge.
