Cron is a compact schedule syntax, not a complete job-management system. A reliable schedule also needs an identified implementation, time-zone policy, missed-run behavior, overlap control, observability, retries, and an idempotent job. This guide treats the expression and the scheduler as separate contracts.
Key Takeaways
- Five-field Unix-like crontab, six/seven-field Quartz, and JavaScript libraries are not interchangeable.
?,L,W, and#are dialect-specific; validate with the exact scheduler that will run the job.- Calendar schedules are evaluated in a time zone and can encounter DST gaps, repeated times, and skipped or duplicated runs.
- Cron does not automatically provide distributed locking, retries, deduplication, or exactly-once execution.
- Prefer a local, version-pinned validator for private schedules; never paste secrets or internal configuration into an online service.
Identify the Dialect First
| Implementation family | Typical fields | Important differences |
|---|---|---|
Unix-like crontab |
5 | minute, hour, day of month, month, day of week; special syntax varies by daemon |
| Quartz/Spring | 6 or 7 | seconds first; often supports ?, L, W, #, and an optional year |
| Application libraries | 5 or 6 | syntax and time-zone support depend on the package and version |
A six-field string beginning with 0 may be a Quartz “at minute zero” schedule, but it is not a valid five-field crontab entry. Put the dialect and time zone beside every schedule in code review and documentation.
Unix-Like Five-Field Syntax
minute hour day-of-month month day-of-week
0 9 * * 1-5
Common operators are * (all allowed values), , (list), - (range), and / (step). Names such as MON and the meaning of Sunday as 0 or 7 depend on the implementation.
Examples for a typical five-field crontab:
| Expression | Intended schedule |
|---|---|
*/5 * * * * |
every five minutes |
0 9 * * 1-5 |
09:00 on weekdays |
0 0 1 * * |
midnight on the first day of each month |
30 4 1,15 * * |
04:30 on the first and fifteenth |
When both day-of-month and day-of-week are restricted, many Vixie-cron descendants run when either field matches, not only when both match. Confirm the daemon documentation before scheduling a narrow calendar event.
Quartz and Library Dialects
Quartz commonly uses:
second minute hour day-of-month month day-of-week [year]
0 0 9 ? * MON-FRI
Here ? means “no specific value” in one of the two calendar fields. L, W, and # may be accepted by Quartz but rejected by a Unix daemon or a library. node-cron, APScheduler, Spring, Kubernetes controllers, and cloud schedulers each have their own parser and time-zone rules. Treat a parser’s documentation and tests as authoritative.
Code Examples
Crontab with a Bounded Environment
SHELL=/bin/sh
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=
# Use an absolute executable and make the job idempotent.
0 2 * * * /opt/jobs/backup --date=today >>/var/log/backup.log 2>&1
Do not assume the interactive shell’s PATH, working directory, locale, credentials, or environment variables. Use a dedicated service account, restrictive file permissions, and a log rotation policy. Avoid crontab -r in automation because it removes the entire user crontab.
Node.js (node-cron, verify the installed version)
const cron = require('node-cron');
const expression = '*/5 * * * *';
if (!cron.validate(expression)) {
throw new Error('invalid node-cron expression');
}
let running = false;
cron.schedule(expression, async () => {
if (running) return; // Prefer a distributed lock for multiple instances.
running = true;
try {
await runIdempotentJob();
} finally {
running = false;
}
});
An in-process flag only protects one process. Multiple replicas need a database lease, queue uniqueness key, or scheduler-level concurrency policy.
Python with an Explicit Time Zone
from datetime import datetime
from zoneinfo import ZoneInfo
from croniter import croniter
zone = ZoneInfo("America/New_York")
base = datetime.now(zone)
schedule = croniter("0 9 * * 1-5", base)
next_run = schedule.get_next(datetime)
print(next_run.isoformat())
croniter parses a five-field expression; it does not turn a schedule into a distributed worker. Use an aware datetime, pin the library version, and test the behavior around DST transitions.
Spring/Quartz-Style Java
@Scheduled(
cron = "0 0 9 * * MON-FRI",
zone = "America/New_York"
)
public void runDailyReport() {
reportService.runIdempotently();
}
The six-field expression above includes seconds. Spring’s parser and Quartz’s parser are related but not identical across versions; compile and test the exact dependency used by the service.
Time Zones and DST
A schedule such as “09:00” is incomplete without a zone. Decide whether it means a civil time in a business zone or a UTC instant. During a spring-forward transition, a local time may not exist; during a fall-back transition, it may occur twice. Implementations may skip, delay, or run twice, and some read the host zone while others accept a per-job zone.
Document:
- IANA zone identifier, not only an abbreviation;
- behavior for nonexistent and repeated local times;
- behavior after downtime or missed runs;
- whether a schedule is evaluated by the host, container, library, or control plane.
Do not rely on a universal CRON_TZ setting. Verify whether the installed daemon supports it and whether it applies to subsequent entries.
Reliability Beyond the Expression
Cron starts an attempt; it does not guarantee successful completion:
- Make the job idempotent with a business key or run identifier.
- Set a timeout and record start, finish, outcome, and scheduler identity.
- Use a lock or queue uniqueness policy to prevent overlapping runs.
- Define retry and backoff behavior outside the expression.
- Decide whether missed runs are skipped, replayed once, or replayed for every interval.
- Alert on absence as well as failure; a silent scheduler is an outage.
For multi-host systems, prefer a managed scheduler or queue when you need leader election, durable retries, concurrency limits, audit history, or dependency graphs.
Validation and Testing
Test the parser and the next-run sequence with fixtures:
dialect: "five-field-crontab"
zone: "Europe/Berlin"
expression: "0 9 * * 1-5"
assert:
- "next run is a weekday civil time"
- "DST gap and repeat are explicitly covered"
- "the job key prevents duplicate processing"
Validate syntax in CI with the same library or daemon version used in production. Test month ends, leap days, Sunday conventions, DST transitions, process restarts, lock expiry, timeouts, retries, and malformed configuration.
Frequently Asked Questions
Why does my expression work in one tool but not another?
Because Cron is a family of dialects. Compare field count, field order, supported operators, day-field semantics, and time-zone handling. Then run the exact parser used in production.
How do I schedule a local-time job safely?
Choose an IANA zone, use an aware time implementation, document DST behavior, and test nonexistent/repeated times. If the business rule is an instant rather than a civil time, store and schedule UTC instants instead.
Does Cron prevent overlapping jobs?
No. Use a lock, queue uniqueness, or scheduler concurrency policy. The job must remain safe if two attempts overlap anyway.
Can Cron run every second?
Traditional crontab cannot. A six-field application scheduler may support seconds, but verify its parser and resource behavior. High-frequency work is often better represented by a long-running worker or queue.
Should I use an online Cron generator?
Only for public or synthetic expressions after checking the service policy. Private schedules can reveal hostnames, internal paths, tenant names, or operational windows; local validation is safer.
Further Reading
- Date Calculator and Civil-Time Boundaries
- Timestamp Conversion and Time Zones
- Agent Workflow Scheduling and Reliability
Conclusion
A Cron expression is the beginning of a scheduling design, not the reliability mechanism itself. Identify the dialect, make the time zone and DST policy explicit, validate with the production parser, and build idempotency, locking, retries, observability, and recovery around the job. That is what turns a five-field string into a dependable operation.