Every 20 Seconds
*/20 * * * * * 0/20 * * * * ? Runs every 20 seconds — at :00, :20, :40 of each minute. Requires a seconds-aware dialect (Spring or Quartz); standard Unix cron is minute-precision only.
Use in your stack
# crontab has no seconds field — three entries tile the minute at
# :00, :20 and :40. flock -n skips rather than stacks a slow run.
* * * * * flock -n /tmp/probe.lock /usr/local/bin/probe.sh
* * * * * sleep 20; flock -n /tmp/probe.lock /usr/local/bin/probe.sh
* * * * * sleep 40; flock -n /tmp/probe.lock /usr/local/bin/probe.sh
# Or a systemd timer — /etc/systemd/system/probe.timer
# Without AccuracySec the 1min default coalesces all three firings.
# [Timer]
# OnCalendar=*-*-* *:*:00/20
# AccuracySec=1s
{
"schedule": {
"cron": "0/20 * * * * ?",
"timezone": "UTC"
}
}
// 6-field: second minute hour day-of-month month day-of-week
@Scheduled(cron = "*/20 * * * * *")
public void everyTwentySeconds() {
// ...
}
// Simpler for pure intervals — no cron parser needed:
@Scheduled(fixedRate = 20000) // milliseconds
public void everyTwentySecondsFixedRate() {
// ...
}
Next runs
Pick a timezone to see when this expression fires next.
- 012026-09-09T15:00:00.000Z
- 022026-09-09T15:00:20.000Z
- 032026-09-09T15:00:40.000Z
- 042026-09-09T15:01:00.000Z
- 052026-09-09T15:01:20.000Z
- 062026-09-09T15:01:40.000Z
- 072026-09-09T15:02:00.000Z
- 082026-09-09T15:02:20.000Z
- 092026-09-09T15:02:40.000Z
- 102026-09-09T15:03:00.000Z
Variations
Common use cases
- Polling a moderately active queue or message broker.
- Refreshing a live metric or status panel during development.
- Sub-minute health checks with a 20-second detection window.
- Periodically flushing a small buffer to a downstream system.
Gotchas
- **Unix crontab cannot do this in one line.** The 5-field format has no seconds field; its minimum is `* * * * *` (every minute). Kubernetes CronJob and GitHub Actions inherit the same limit. Three offset entries get you there — see the Linux tab above.
- **A systemd timer here needs `AccuracySec=1s`.** It defaults to **1 minute**, so `OnCalendar=*-*-* *:*:00/20` on its own lets systemd coalesce the three firings into one. The unit looks correct and runs a third as often as intended.
- In Spring, `*/20 * * * * *` is the 6-field form (seconds first). Don't confuse it with Unix's 5-field `*/20 * * * *`, which means every 20 *minutes*.
- In Quartz, use `0/20 * * * * ?` — the `0/20` seconds field means "starting at second 0, every 20 seconds," and `?` fills the unused day-of-week field.
- For a pure interval, a `fixedRate`/`fixedDelay` loop is simpler than a cron parser. Use `@Scheduled(fixedRate = 20000)` in Spring or `tokio::time::interval` in Rust.
- Because 60 divides evenly by 20, the runs land cleanly at :00, :20, :40 — no drift within the minute.
Three times a minute is an awkward cadence for cron, because cron’s finest unit is the minute itself. The 5-field format in /etc/crontab — and in Kubernetes CronJobs and GitHub Actions, which borrow it — starts counting at minutes and has nowhere to put a 20.
On Linux, without changing schedulers
Sixty divided by twenty is three, so three crontab lines cover the minute exactly: one on the minute, then sleep 20 and sleep 40 (full block in the Linux tab above). Three is a comfortable number here — the crontab stays readable and you get three log streams rather than the twelve a 5-second job would produce.
What you’re trading away is precision. sleep 20 begins counting when cron forks the job, not at the top of the minute, so on a loaded box the two offset copies gradually slide. Wrapping each in flock -n at least guarantees a run that overshoots its 20-second budget gets dropped rather than piling onto the next.
If the machine runs systemd, skip all of that:
[Timer]
OnCalendar=*-*-* *:*:00/20
AccuracySec=1s
The AccuracySec line is doing real work. Leave it out and it falls back to 1 minute, which permits systemd to fold all three firings into a single run — a unit file that reads exactly right while firing a third as often as you intended. systemd timers covers the timer+service pair this needs.
A stagger note
Twenty seconds is a useful interval precisely because it doesn’t line up with the common ones. A 20-second job and a 30-second job share only the top of the minute; a 20 and a 15 share only :00 as well. If you’re spacing out health checks so they don’t all fire together and spike load, that non-alignment is the reason to choose 20 over 15 or 30.
Or use a dialect that has seconds
To go sub-minute you need a 6-field, seconds-first dialect:
- Spring
@Scheduled(cron = "*/20 * * * * *")— see the Spring cron reference and the Spring @Scheduled guide. - Quartz
0/20 * * * * ?— see the Quartz cron reference.
For an interval this tight, a plain timer is usually the better tool. Spring’s @Scheduled(fixedRate = 20000) and Rust’s tokio::time::interval(Duration::from_secs(20)) both express “every 20 seconds” without a cron parser — and they’re easier to reason about when a run might overrun its window. See also every 15 seconds and every 30 seconds.
See sub-minute scheduling for which runtimes support seconds at all, and what to do on the ones that don’t.
Frequently asked questions
What is the cron expression for every 20 seconds?
How do I run a cron job every 20 seconds on Linux?
Why doesn't `*/20 * * * *` run every 20 seconds?
Should I use a 20-second interval to stagger jobs?
How do I run something every 20 seconds in Spring Boot?
Can Kubernetes CronJob run a job every 20 seconds?
More seconds schedules
Other patterns on the same cadence — or browse every schedule.