How to run a cron job every 30 seconds
Cron can’t run anything more often than once a minute: the smallest field in a crontab line is the minute. There are still several reliable ways to run a job every 30, 15 or 10 seconds. Which one fits depends on how long the job takes and whether two runs may overlap.
Two crontab lines with sleep
The most common trick: run the job at the start of every minute, and again 30 seconds later.
* * * * * /srv/poll.sh * * * * * sleep 30; /srv/poll.sh
It is simple and survives reboots like any cron job. The second run starts 30 seconds after the minute, not 30 seconds after the first run ended, so if poll.sh can take longer than 30 seconds, two copies will run at once. Prevent that with flock:
* * * * * flock -n /tmp/poll.lock /srv/poll.sh * * * * * sleep 30; flock -n /tmp/poll.lock /srv/poll.sh
-n makes the second copy give up right away instead of waiting, so runs are skipped rather than queued.
A loop inside one minute
For 15 or 10 seconds, more lines get messy. Use a loop instead:
# every 15 seconds
* * * * * for i in 1 2 3 4; do /srv/poll.sh; sleep 15; done
Here each run starts after the previous one finished plus 15 seconds, so a slow run pushes the next ones later. If the loop doesn’t fit in a minute, it overlaps with the next minute’s loop. That’s fine for quick jobs and a reason to pick another option for slow ones.
A systemd timer
systemd timers support seconds directly, and a timer never starts a service that is still running:
# /etc/systemd/system/poll.timer
[Timer]
OnBootSec=30s
OnUnitActiveSec=30s
AccuracySec=1s
[Install]
WantedBy=timers.target
AccuracySec=1s matters: by default systemd may delay a timer by up to a minute to save power, which defeats a 30-second interval. The service itself is a normal Type=oneshot unit; the systemd timers guide shows a complete one with pings for success and failure.
A long-running worker
If the job runs every few seconds, starting a new process each time costs more than the work. A small loop kept alive by systemd, Docker or Kubernetes is often cleaner:
#!/bin/sh while true; do /srv/poll.sh sleep 30 done
Run it with Restart=always in a systemd service, or restart: unless-stopped in Docker Compose. Then the process supervisor, not cron, keeps it running.
Which one to choose
| Option | Good for | Watch out for |
|---|---|---|
| Two lines with sleep | Quick jobs every 30 seconds | Overlaps without flock |
| Loop in one line | Quick jobs every 10–20 seconds | Drift and overlap when a run is slow |
| systemd timer | Any interval, no overlaps | Set AccuracySec |
| Long-running worker | Every few seconds | Needs a supervisor to restart it |
Monitoring a job that runs this often
A job that runs every 30 seconds fails loudly when it fails, but it can also just stop: the timer is disabled, the worker dies, the server is replaced and the crontab isn’t restored. A heartbeat catches all of these. You don’t need a ping on every run; one ping a minute is enough to know the job is alive.
In Heartcron, create a check with a 1-minute period and a short grace period, and ping from the first crontab line only:
heartcron checks create poller --every 1m --grace 2m * * * * * flock -n /tmp/poll.lock /srv/poll.sh && curl -fsS -m 10 --retry 3 https://ping.heartcron.com/<uuid> > /dev/null * * * * * sleep 30; flock -n /tmp/poll.lock /srv/poll.sh
If pings stop for more than three minutes, you get an alert by email, Telegram, Slack or Discord. Every signal, including /fail for errors, is in the docs. For schedules of a minute and longer, the cron expression explainer shows exactly when a line will run.