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

· 3 min read · DevOps Tutorials

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 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. 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):

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).

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.

#Cron #Linux #Automation #Kubernetes