The short answer
Standard cron has no seconds field. Its five fields start at the minute, and cron checks the schedule once a minute, so no expression means "every 30 seconds". The closest it gets is every minute:
* * * * *
There are three usual ways to get a 30-second interval anyway.
Workaround 1: two crontab lines with sleep
* * * * * /usr/local/bin/job.sh
* * * * * sleep 30; /usr/local/bin/job.sh
Both lines fire at the start of every minute. The first runs the job straight away; the second waits 30 seconds and then runs it, so you get runs at :00 and :30 of every minute. For every 20 seconds, use three lines with sleep 20 and sleep 40; for every 15 seconds, four lines.
| Field | Value | Meaning |
|---|---|---|
| Minute | * |
every minute |
| Hour | * |
every hour |
| Day of month | * |
every day |
| Month | * |
every month |
| Day of week | * |
every day of the week |
The interval is approximate: cron starts a little after the minute boundary, and if the job takes 10 seconds, the gaps are no longer an even 30. If a run can take more than 30 seconds, guard both lines with the same lock: flock -n /tmp/job.lock /usr/local/bin/job.sh.
Workaround 2: a systemd timer
On Linux with systemd, a timer can fire on seconds. Create job.service (with ExecStart=/usr/local/bin/job.sh) and job.timer:
[Timer]
OnCalendar=*:*:0/30
AccuracySec=1s
[Install]
WantedBy=timers.target
*:*:0/30 means second 0 and 30 of every minute. Set AccuracySec: its default is one minute, which lets systemd shift runs by up to that much. Enable it with systemctl enable --now job.timer. For "30 seconds after the last run finished" instead of wall-clock times, use OnUnitActiveSec=30s plus OnBootSec=30s. A timer never starts a second copy while the service is still running.
Workaround 3: a scheduler with seconds
Quartz (Java) and Spring use six fields with seconds first: 0/30 * * * * ? in Quartz, */30 * * * * * in Spring's @Scheduled(cron = …). Node's node-cron also accepts an optional seconds field. A small loop in a long-running process (while true; do job; sleep 30; done under a supervisor) works too.
Where it won't work
- GitHub Actions: no seconds, and the shortest interval is 5 minutes.
- Kubernetes CronJob: standard five fields only, so one minute at best; run a Deployment with a loop instead.
- AWS EventBridge: the minimum is one minute,
rate(1 minute). For 30 seconds, have a Step Functions state machine or a worker wait between runs.
Related: every minute, every 2 minutes, every 5 minutes.
Check the per-minute base schedule in the Cron Expression Parser.