Cron Expressions with Examples: crontab, Kubernetes CronJob, GitHub Actions and EventBridge
What is a cron expression #
Cron runs a command on a schedule. The schedule is a cron expression of five fields separated by spaces:
┌───────── 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.
crontab #
Each user has a crontab:
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:
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
PATHat 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. Writedate +\%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. 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):
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).
Cron in GitHub Actions #
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.
Cron in 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:
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:
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.