Engineering · August 24, 2026

Cron expression cheatsheet — every example you'll actually need

Every minute, every weekday at 9am, first of the month, last Friday — copy-paste cron expressions with explanations of what every field does.

A cron expression is the most concise way to tell a computer “run this thing on this schedule”. Five fields, all numbers and special characters, no ambiguity once you learn the syntax. The problem: there are about fifteen common patterns you actually need, and the rest of the time you’re staring at a crontab -e prompt trying to remember if Sunday is 0 or 7.

This post is the cheatsheet I wish I’d had five years ago. Every common pattern, with a copy-paste expression, an explanation, and a note on the gotcha that catches people.

The five fields

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

Each field can be:

  • A specific value: 5
  • A wildcard: * (every value)
  • A range: 1-5
  • A step: */15 (every 15)
  • A list: 1,15,30
  • A range with step: 1-30/5 (every 5 between 1 and 30)

That’s it. The rest is combining those primitives.

The patterns

Every minute

* * * * *

Use case: a job that should always be running and the scheduler is just respawning it. Rarely what you want.

Every N minutes

*/5 * * * *      # every 5 minutes
*/15 * * * *     # every 15 minutes
*/30 * * * *     # every 30 minutes

The */5 means “starting at 0, every 5 minutes”. So 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55.

Gotcha: the first run of */5 is at minute 0, not at the moment you saved the crontab. If you save it at 12:03, the first run is 12:05, not 12:08.

Every hour on the hour

0 * * * *

Gotcha: this is “minute 0 of every hour”, which is 1:00, 2:00, 3:00, etc. If you want “every hour, 30 minutes past”, use 30 * * * *.

Every day at midnight

0 0 * * *

Gotcha: the server’s timezone. cron uses the system timezone. If your server is in UTC and you want midnight in US Eastern, you need to convert. The cleanest fix: configure the system timezone to what you want, or set the CRON_TZ environment variable (supported by most modern cron implementations).

Every day at a specific time

0 9 * * *       # 9:00 AM
30 14 * * *     # 2:30 PM
0 0 * * *       # midnight
15 3 * * *      # 3:15 AM

Every weekday at 9 AM

0 9 * * 1-5

The 1-5 is day-of-week, Monday through Friday.

Gotcha: 0 and 7 both mean Sunday. The POSIX spec accepts both. Most implementations are forgiving if you accidentally use 7. Don’t rely on that.

Every Monday

0 0 * * 1

Midnight every Monday.

Twice a week (Mon and Thu)

0 9 * * 1,4

9 AM on Monday and Thursday.

First of every month

0 0 1 * *

Midnight on the 1st.

Last day of the month

0 0 L * *

The L is a Vixie cron / Quartz extension. Standard cron doesn’t have it. If you’re using standard cron, you’re stuck with “last day” approximations like 0 0 28-31 * * (which runs on the 28th, 29th, 30th, and 31st, and on the 28th of February).

Every quarter

0 0 1 */3 *

Midnight on the 1st of January, April, July, October.

Every Sunday at noon

0 12 * * 0

Every 6 hours

0 */6 * * *

0:00, 6:00, 12:00, 18:00.

Every minute between 9 AM and 5 PM, weekdays

* 9-17 * * 1-5

A job that runs every minute from 9:00:00 to 17:59:59, Monday through Friday. Probably too aggressive — but possible.

At 2:30 AM on the first of every month

30 2 1 * *

At 11 PM on the last Friday of the month

0 23 * * 5#5

The 5#5 is “5th Friday of the month” (Quartz syntax). The # and L are non-standard extensions — standard cron doesn’t have a “last Friday of the month” expression. If you need this in standard cron, you need a wrapper script that checks the date.

Every 30 seconds

Standard cron doesn’t go below 1 minute. For sub-minute scheduling:

  • systemd timers: OnUnitActiveSec=30s
  • In Kubernetes CronJob: not possible (1-minute minimum). Use a regular Job with sleep 30 in a loop.
  • In a Node.js app: setInterval(fn, 30_000) inside the process.

Daylight saving time

cron handles DST by either running the job twice (spring forward) or skipping it (fall back), depending on the implementation. If your job is time-sensitive (a billing run, a report), schedule it well outside the 1-3 AM window to avoid DST surprises. Use our timestamp converter to quickly convert between Unix timestamps and human-readable dates when debugging schedule issues.

The Quartz scheduler handles this with the DST flag. Standard cron does not.

The 1-minute granularity limit

cron is fundamentally a 1-minute-resolution tool. If you need sub-minute scheduling, the answer is “use a different tool”. For Kubernetes specifically:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: every-30-seconds
spec:
  schedule: "* * * * *"
  startingDeadlineSeconds: 10
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: worker
              image: myworker:latest
              command: ["/bin/sh", "-c", "for i in 1 2; do do_work; sleep 30; done"]

This runs the job every 30 seconds by issuing two sub-jobs per minute. Crude, but it works.

What about Quarkus, EventBridge, Kubernetes CronJob?

Different schedulers use slightly different cron syntax. The five-field form is the most common. Some others:

  • Quartz: six fields (with seconds), and the ? wildcard for “no specific value”.
  • AWS EventBridge: six fields, supports the L and # extensions.
  • Kubernetes CronJob: five fields, no L or # support.
  • Spring @Scheduled: six fields (with seconds), ? and L support.
  • GitHub Actions: five fields, no extensions.

If you’re using a tool that doesn’t match the standard, check the docs. Most of the patterns in this cheatsheet transfer directly. The non-standard extensions (L, #, W) don’t.

Generating cron expressions without memorising syntax

If you don’t want to memorise the syntax, the cron generator on DevSpeedTools lets you click through the five fields and shows you the next five scheduled runs. It’s the fastest way to verify “does 0 9 * * 1-5 really mean 9 AM weekdays?” — the answer is yes, but seeing the next run dates makes the pattern concrete.

For one-off expressions, crontab.guru is the standard reference. The cron generator does the same job and is fully client-side, so the expressions you paste never leave your browser.

The TL;DR

Cron expressions are a tiny DSL with a few rules. Learn the five fields, learn * (any), - (range), / (step), and , (list), and you can express 95% of the schedules you’ll ever need. The other 5% need scheduler-specific extensions or a different tool. When in doubt, generate and preview the expression with a tool that shows the next runs — much faster than mental debugging.