~/cron ☀ LIGHT apps ← about me

🌐 Language

// crontab · linux · kubernetes · cloud scheduler
██████╗ ██████╗ ██████╗ ███╗ ██╗ ██╔════╝ ██╔══██╗ ██╔═══██╗ ████╗ ██║ ██║ ██████╔╝ ██║ ██║ ██╔██╗ ██║ ██║ ██╔══██╗ ██║ ██║ ██║╚██╗██║ ╚██████╗ ██║ ██║ ╚██████╔╝ ██║ ╚████║ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝

Cron expression explainer_

Paste any cron expression — get an instant plain-English explanation and the next 10 run times.
Supports standard 5-field and extended 6-field (with seconds) syntax.

minute
hour
day (month)
month
day (week)
// next 10 scheduled runs (UTC)
Minute (0–59)
*every minute
0at minute 0 (top of hour)
*/15every 15 minutes
0,30at minute 0 and 30
5-10minutes 5 through 10
Hour (0–23)
*every hour
0midnight
12noon
9-17business hours
*/6every 6 hours
Day of Month (1–31)
*every day
1first of month
Llast day of month
1,151st and 15th
15Wnearest weekday to 15th
Month (1–12)
*every month
1January
6June
*/3every quarter
1,7Jan and Jul
Day of Week (0–7, Sun=0 or 7)
*every day
0Sunday
1-5Monday–Friday
6,0weekend
1Monday
Special strings
stringequivalent
@yearly0 0 1 1 *
@monthly0 0 1 * *
@weekly0 0 * * 0
@daily0 0 * * *
@hourly0 * * * *
@rebooton startup

Cron expressions — every question answered

Sixty-two questions on cron syntax, why steps like */45 are not really intervals, the day-of-month vs day-of-week OR rule that makes jobs fire five times a month, timezones and daylight saving, how crontab differs from systemd timers, Kubernetes CronJobs and GitHub Actions, and why a script that works in your shell does nothing under cron.

01. Using this tool

What does this tool do?

Paste any cron expression and it returns a plain-English description of the schedule plus the next ten run times, so you can see immediately whether 0 0 1 * 1 means what you think it means. It reads standard five-field cron and the extended six-field form with seconds.

Do I have to build the expression by hand?

No. The reference cards list every field with its allowed values, and the special-string table is clickable — pick @daily or @hourly and the expression loads. From there you can edit a field and watch the description and the run times change.

Why show the next ten runs instead of just describing it?

Because a description can still be misread, and the run times cannot. “At 00:00 on day-of-month 1 and on Monday” sounds reasonable until you see the actual dates and realise the job fires far more often than intended. The run list is the fastest way to catch a wrong schedule before it reaches production.

Can I link to a specific expression?

Yes — ?expr=0+9+*+*+1-5 loads that expression already explained. Spaces can be + or %20, special strings work (?expr=@daily), and ?cron= and ?q= are accepted as aliases. The copy shareable link button builds the URL for whatever is on screen, which makes it easy to paste an explanation into a pull request or a ticket.

Is anything sent to a server?

No. Parsing and the run-time calculation happen in your browser. Nothing is uploaded and nothing is stored — useful, because a cron line often reveals internal script paths and host names.

Is it free?

Free, no account, no limits. One of a set of client-side tools at jasperbernaers.com/apps.

02. Cron syntax basics

What is a cron expression?

Five space-separated fields describing when a job should run: minute hour day-of-month month day-of-week. 30 2 * * * means 02:30 every day. It is the scheduling language used by Unix cron and copied by GitHub Actions, Kubernetes, Azure Functions, AWS EventBridge and most CI systems.

What are the allowed values for each field?
  • Minute — 0–59
  • Hour — 0–23
  • Day of month — 1–31
  • Month — 1–12, or JAN–DEC
  • Day of week — 0–7, or SUN–SAT (both 0 and 7 mean Sunday)
What does the asterisk mean?

* means “every value in this field”. * * * * * runs every minute of every day. The most common beginner mistake is leaving the minute field as * when you meant a specific minute: * 3 * * * runs sixty times between 03:00 and 03:59, not once at 03:00.

How do I run a job every hour?

0 * * * * — at minute 0 of every hour. Every day at midnight is 0 0 * * *. Every thirty minutes is */30 * * * *. Every weekday at 09:00 is 0 9 * * 1-5.

What is the six-field format with seconds?

Some schedulers prepend a seconds field: */15 * * * * * runs every fifteen seconds. Quartz, Spring's @Scheduled, Azure Functions and several job libraries use six fields. Plain Unix cron does not — its finest resolution is one minute.

Why does the same expression behave differently in different tools?

Because “cron” is a family of dialects rather than one specification. Vixie cron (the Linux default), Quartz, Kubernetes, AWS EventBridge and Spring all differ in field count, day-of-week numbering and supported characters. Always check which flavour your scheduler documents before copying an expression between systems.

Are month and day names allowed?

In most implementations, yes: 0 0 * JAN MON is valid in Vixie cron. Names are case-insensitive and three letters. They are also not portable everywhere, so numbers are the safer choice in anything shared between systems.

Does cron support seconds in Linux?

No. The smallest interval standard cron can express is one minute. For anything faster, use a systemd timer with OnUnitActiveSec, a loop with sleep inside a long-running service, or a scheduler built for sub-minute work.

03. Ranges, steps and lists

What does a range like 1-5 mean?

Every value from the first to the last, inclusive. 0 9 * * 1-5 is 09:00 Monday through Friday. Ranges can wrap conceptually in some implementations but not reliably — 5-1 for Friday to Monday is not portable, and should be written 5,6,0.

What does the slash mean?

A step. */15 in the minute field means every fifteenth minute starting at 0 — so :00, :15, :30 and :45. It can be combined with a range: 0-30/10 means minute 0, 10, 20 and 30 only.

Is */20 really every twenty minutes?

Within the hour, yes: 0, 20 and 40. But the step restarts every hour, so the gap between the run at :40 and the next at :00 is also twenty minutes — that one happens to work. */45 does not: it fires at :00 and :45, then again at :00, giving gaps of 45 and 15 minutes. Steps that do not divide 60 evenly never produce an even interval.

How do I run something every two hours?

0 */2 * * * — at minute 0 of hours 0, 2, 4 … 22. Because 24 is divisible by 2 the interval is genuinely even. For every five hours, 0 */5 * * * fires at 0, 5, 10, 15 and 20 and then jumps four hours to midnight.

What does a comma-separated list do?

Lists specific values: 0 8,12,17 * * * runs at 08:00, 12:00 and 17:00. Lists, ranges and steps can be mixed in one field — 0 9-17/2,20 * * * is valid, and also the point at which a comment above the line becomes a kindness.

How do I run a job every quarter?

0 0 1 1,4,7,10 * — midnight on the first day of January, April, July and October. Using a list of months is clearer than */3, which starts from January anyway but reads as an interval rather than a set of quarters.

How do I run something on the last day of the month?

Standard cron cannot express it. Two options: use L if your scheduler supports Quartz-style syntax, or schedule daily and exit early unless it is the last day — 0 23 28-31 * * [ "$(date -d tomorrow +%d)" = "01" ] && /path/script. The second is portable and works everywhere.

How do I run a job on the first Monday of the month?

Also not expressible in standard cron. The classic trick is to run every Monday and test the date: 0 9 1-7 * 1 /path/script — days 1–7 combined with Monday. Careful: in Vixie cron this fires on every day from 1–7 as well as every Monday, because of the OR rule. The reliable version is 0 9 * * 1 plus a [ "$(date +%d)" -le 07 ] guard in the script.

04. Day-of-month vs day-of-week — the classic trap

Why did my job run far more often than expected?

Almost certainly the OR rule. When both the day-of-month and day-of-week fields are restricted (neither is *), cron runs the job when either matches, not both. 0 0 1 * 1 means “midnight on the 1st of the month or every Monday” — roughly five times a month, not once.

How do I run a job only when both day conditions match?

You cannot, in the expression alone. Restrict one field and test the other in the script: schedule 0 0 * * 1 for every Monday and start the script with [ "$(date +%d)" -le 07 ] || exit 0 to keep only the first Monday. It is the standard workaround and it is what every scheduling library does internally.

When does the OR rule not apply?

When one of the two fields is *. 0 9 * * 1-5 and 0 9 15 * * both behave intuitively, because only one day field is restricted. The surprise only appears when both carry a real value.

Is the OR rule the same in every scheduler?

No, and that is the dangerous part. Vixie cron and most Unix implementations use OR. Quartz forbids specifying both fields at all and requires ? in one of them. Some libraries use AND. An expression copied between systems can silently change meaning, which is a good reason to check the run list here after moving one.

What does the ? character mean?

“No specific value”, used in Quartz-style cron to say that one of the two day fields is irrelevant: 0 0 12 ? * MON. Standard Unix cron does not accept ? — if a copied expression fails to parse, this is often why.

Is Sunday 0 or 7?

Both, in Unix cron. Day-of-week accepts 0–7 where 0 and 7 are Sunday. Quartz numbers 1–7 with Sunday as 1, so MON is 2 there and 1 in Unix cron. Copying a weekday expression between the two shifts every day by one — a bug that only shows up on the wrong day of the week.

05. Special strings and extensions

What are the @ shortcuts?
  • @yearly / @annually0 0 1 1 *
  • @monthly0 0 1 * *
  • @weekly0 0 * * 0
  • @daily / @midnight0 0 * * *
  • @hourly0 * * * *
  • @reboot — once when cron starts
Should I use @daily or 0 0 * * *?

They are identical in Vixie cron, and @daily is more readable. Two caveats: not every scheduler supports the shortcuts (Kubernetes CronJob does, AWS EventBridge does not), and everything on @daily fires at exactly midnight, which is how you end up with fifty jobs competing at 00:00. Spreading them out is worth the extra characters.

What does @reboot actually do?

Runs the job once when the cron daemon starts, which is usually but not always at boot. It does not re-run if the daemon is restarted for an unrelated reason, and it runs before many services are ready. For anything with dependencies, a systemd unit with proper After= ordering is the better tool.

What is the L character?

Quartz-style “last”: L in day-of-month means the last day of the month, 5L in day-of-week means the last Friday. Standard Unix cron does not support it. Kubernetes CronJob does not either.

What are W and # in cron expressions?

Quartz extensions. 15W means the weekday nearest the 15th — useful for payroll runs that must not land on a Saturday. 6#3 in day-of-week means the third Friday of the month. Neither works in Linux crontab.

What is a random or hashed schedule?

Some systems accept H in place of a value — Jenkins is the well-known one. H 2 * * * means “some consistent minute within hour 2”, chosen from a hash of the job name, so hundreds of jobs spread across the hour instead of all firing at :00. GitHub Actions does not support it but does silently delay jobs scheduled at popular times, which achieves something similar less predictably.

06. Timezones, DST and drift

Which timezone does cron use?

The system timezone of the machine running it, unless configured otherwise. That is fine until the server is in UTC and you assumed local time — a 09:00 job then runs at 10:00 or 11:00 depending on the season. Always check timedatectl or date on the host before trusting a schedule.

How do I set a timezone for a crontab?

Many cron implementations accept a CRON_TZ=Europe/Brussels line above the entries, or a TZ= variable. Support varies: Vixie cron and cronie honour CRON_TZ, some minimal container crons ignore both. The portable alternative is to compute UTC yourself and put UTC in the expression.

What happens to cron jobs during daylight saving changes?

In spring, when the clock jumps from 02:00 to 03:00, a job scheduled at 02:30 does not run at all on most implementations. In autumn, when 02:00–03:00 happens twice, it may run twice. Modern Vixie cron tries to compensate for jobs in that window, but the behaviour is implementation-specific and not something to rely on.

How do I make a schedule immune to DST?

Run the machine and the scheduler in UTC, and convert when displaying to humans. If the job genuinely must follow local wall-clock time — a report that has to be on someone's desk at 08:00 local — accept that the UTC time shifts twice a year and set the expression accordingly, or use a scheduler that supports timezone-aware schedules.

Why does my Kubernetes CronJob run at the wrong time?

Because CronJob schedules are evaluated in the controller manager's timezone, historically UTC. Newer Kubernetes versions support a timeZone field on the CronJob spec; without it, write your expression in UTC.

Does GitHub Actions use my repository's timezone?

No — schedule triggers are always UTC, with no timezone option. A workflow set to 0 9 * * 1-5 runs at 10:00 or 11:00 Brussels time depending on the season. Runs are also queued rather than guaranteed on time, and can be delayed during peak load.

Why do my jobs drift over time?

Cron itself does not drift — it fires on wall-clock boundaries. Drift comes from the job taking longer than its interval, so runs overlap or queue. If a five-minute job is scheduled every five minutes, one slow run makes every subsequent run late. Use a lock file, or schedule less often than the worst-case runtime.

07. Where cron runs: crontab, systemd, containers and cloud

How do I edit a crontab?

crontab -e edits the current user's crontab, crontab -l lists it, crontab -r deletes it entirely — a keystroke away from -e and with no confirmation, which is worth knowing before you type it. System-wide entries live in /etc/crontab and /etc/cron.d/.

What is the difference between a user crontab and /etc/cron.d?

A user crontab has five fields and runs as its owner. Files in /etc/cron.d/ and /etc/crontab have a sixth field for the username between the schedule and the command: 0 3 * * * backup /usr/local/bin/run.sh. Pasting a user crontab line into /etc/cron.d without the user field is a very common failure.

Why does my crontab entry not run when the file looks correct?

The classic causes, in order of frequency: no trailing newline at the end of the file, the file in /etc/cron.d has a dot in its name (cron ignores it), wrong file permissions, or the crontab was edited with an editor that left it without a final line break. crontab -e avoids most of these.

Why does my script work in the shell but not in cron?

Environment. Cron runs with a minimal environment — often just PATH=/usr/bin:/bin, no ~/.bashrc, no nvm, no virtualenv, no DISPLAY. Use absolute paths for every binary, set PATH explicitly at the top of the crontab, and source whatever your script needs rather than assuming it is there.

What does the percent sign do in a crontab?

An unescaped % is treated as a newline and everything after the first one is fed to the command on stdin. This breaks any command containing a date format string: write date +\%Y-\%m-\%d with escaped percent signs, or move the command into a script.

Where does cron output go?

To email, by default — cron mails stdout and stderr to the owner via the local MTA. On a machine with no mail configured, that output is simply lost, which is why jobs fail silently. Redirect explicitly: >> /var/log/myjob.log 2>&1, and never use > /dev/null 2>&1 unless you genuinely do not care whether it worked.

Should I use systemd timers instead of cron?

On a modern Linux system, often yes. Timers give you logging through journald, dependency ordering, Persistent=true to catch missed runs after downtime, randomised delays, resource limits and per-unit status. Cron wins on brevity and on being available everywhere, including minimal containers. A useful split: cron for simple, self-contained jobs; timers for anything that matters.

What is the systemd equivalent of a cron expression?

OnCalendar=, with its own syntax: OnCalendar=Mon..Fri 09:00 or OnCalendar=*-*-01 00:00:00. systemd-analyze calendar "Mon..Fri 09:00" prints the next run times, which is the systemd equivalent of what this page does for cron.

How do cron jobs work in Kubernetes?

A CronJob resource takes a standard five-field schedule and creates a Job per run. Watch three things: the schedule is evaluated in the controller's timezone unless you set timeZone, concurrencyPolicy defaults to Allow so slow jobs overlap, and startingDeadlineSeconds controls what happens after a missed window. Also set successfulJobsHistoryLimit, or completed pods accumulate.

Does Docker have cron?

Not by default — minimal images have no cron daemon and no init system, so a container running only your app has nothing to run the schedule. The options are: install and run cron as the container's main process, use the orchestrator's scheduler (Kubernetes CronJob, ECS scheduled task, Nomad periodic), or schedule from outside the container with docker exec.

Which cloud schedulers use cron syntax?

Most of them, with variations. AWS EventBridge uses six fields with a required ? in one day field and no @ shortcuts. Azure Functions timer triggers use six fields with seconds. Google Cloud Scheduler uses standard five-field with a timezone setting. GitHub Actions uses five-field UTC. Always paste the expression into that provider's own documentation examples before trusting it.

08. Troubleshooting and good practice

My cron job does not run at all. What do I check first?

In order: is the cron daemon running (systemctl status cron or crond); does crontab -l show the entry; is the script executable and does its shebang exist; does the log show an attempt (journalctl -u cron or /var/log/syslog); and does the command run when you paste it into a shell with env -i to simulate cron's empty environment.

How do I test a cron expression without waiting for it?

Paste it here and read the next ten run times — that is the fastest check for the schedule itself. To test the command, run it with a stripped environment: env -i /bin/sh -c '/path/to/script', which reproduces most cron-only failures immediately.

How do I stop overlapping runs?

Wrap the command in flock: * * * * * /usr/bin/flock -n /tmp/myjob.lock /path/script. The -n makes a second instance exit immediately rather than queue. Without it, a job that occasionally runs long will eventually have several copies competing for the same resource.

How do I know a job silently stopped working?

Cron will not tell you. Use a dead-man's switch: have the job ping a monitoring endpoint on success, and alert when the ping stops. Healthchecks.io, Cronitor and equivalent self-hosted tools exist for exactly this, and it is the single highest-value addition to any scheduled job.

Should a cron job send email?

Only if someone reads it. The default behaviour — mail on any output — turns into noise within a week and then into a filter rule. Log to a file, monitor for failure, and keep the mailbox for things that need a human.

What happens to missed runs after downtime?

Plain cron skips them: if the machine was off at 03:00, the 03:00 job simply did not happen. anacron exists to catch up daily, weekly and monthly jobs on machines that are not always on, and systemd timers do the same with Persistent=true. Kubernetes CronJob uses startingDeadlineSeconds to decide whether a late run is still worth starting.

Is it safe to schedule something every minute?

It works, and it is often a sign the job should be a service instead. Every-minute cron gives you no back-pressure, no state between runs and a new process each time. If the work is genuinely continuous, a long-running process with an internal loop — or a queue worker — is usually simpler and cheaper.

How should I schedule backups?

Off the hour, and off midnight. 17 3 * * * is a better backup time than 0 0 * * *, because midnight is contended by everything else and log rotation is often already running. Stagger jobs across machines so they do not all hit the same storage or network path at once.

How do I document a cron expression for the next person?

Put a comment line directly above it with the plain-English meaning — the same sentence this tool generates. # 09:00 Monday to Friday above 0 9 * * 1-5 costs one line and saves the next engineer from decoding it. A share link to the explanation works well in a runbook.

Which browsers does this tool work in?

Any current version of Chrome, Edge, Firefox, Safari, Brave, Opera or Vivaldi, on desktop or mobile. No frameworks and no external libraries — the page is self-contained and keeps working offline.