How to Run a Cron Job More Frequently Than Every Minute (Without Sleep Commands)


2 views

Cron is a powerful time-based job scheduler in Unix-like systems, but it has a fundamental limitation: the smallest time interval it supports is one minute. This becomes problematic when you need to execute tasks more frequently, such as every 30 seconds.

While you could technically create a script that runs every minute and sleeps for 30 seconds between executions, this approach has several drawbacks:

  • Process management becomes messy
  • Error handling is more complex
  • Resource usage is less efficient

Here are three robust approaches to achieve sub-minute scheduling:

1. Using Systemd Timers

Modern Linux distributions with systemd can use timers for more precise scheduling:


# Create a service file
[Unit]
Description=My frequent task

[Service]
ExecStart=/path/to/your/script.sh

# Create a timer file
[Unit]
Description=Run my script every 30 seconds

[Timer]
OnBootSec=30s
OnUnitActiveSec=30s
AccuracySec=1ms

[Install]
WantedBy=timers.target

2. Combining Cron with FIFO

This approach uses cron to trigger a controller script that manages the timing:


#!/bin/bash
# controller.sh
while true; do
  /path/to/actual_task.sh
  sleep 30
done

Then set up your crontab:


* * * * * /path/to/controller.sh

3. Using a Process Supervisor

Tools like supervisord can manage frequent executions:


[program:my_frequent_task]
command=/path/to/your/script.sh
autostart=true
autorestart=true
startsecs=0
startretries=3

When running tasks this frequently:

  • Monitor system resources carefully
  • Consider implementing a queue system if tasks might overlap
  • Log execution times to identify potential bottlenecks

Here's a complete example of a 30-second monitoring script using systemd:


# monitor.service
[Unit]
Description=Server monitor

[Service]
ExecStart=/usr/local/bin/monitor.sh

# monitor.timer
[Unit]
Description=Run monitor every 30 seconds

[Timer]
OnBootSec=30s
OnUnitActiveSec=30s

[Install]
WantedBy=multi-user.target

Enable it with:


sudo systemctl enable monitor.timer
sudo systemctl start monitor.timer

Traditional cron jobs are fundamentally designed to execute tasks at minute-based intervals. The smallest time unit in standard cron syntax is one minute, represented by the first field in the crontab entry. This limitation stems from cron's original design for system maintenance tasks that typically don't require sub-minute precision.

When you need to run tasks more frequently than every minute, consider these reliable methods:

Method 1: Combining Cron with Script Logic

Create a wrapper script that handles the sub-minute execution internally:

#!/bin/bash
# Script: /usr/local/bin/30sec_job.sh

for i in {1..2}; do
  /path/to/actual_script.sh
  if [ $i -lt 2 ]; then
    sleep 30
  fi
done

Then set up your crontab to run this every minute:

* * * * * /usr/local/bin/30sec_job.sh

Method 2: Systemd Timer Units

For modern Linux systems using systemd, create a more precise timer:

# /etc/systemd/system/30sec-job.service
[Unit]
Description=30 Second Job

[Service]
ExecStart=/path/to/your/script.sh

# /etc/systemd/system/30sec-job.timer
[Unit]
Description=Run every 30 seconds

[Timer]
OnBootSec=30
OnUnitActiveSec=30
AccuracySec=1ms

[Install]
WantedBy=timers.target

Enable with:

sudo systemctl enable 30sec-job.timer
sudo systemctl start 30sec-job.timer

When implementing sub-minute cron jobs, be aware of:

  • Resource consumption from frequent process spawning
  • Potential overlapping executions if jobs run longer than interval
  • System load impacts during peak times

Implement robust logging for frequent jobs:

#!/bin/bash
TIMESTAMP=$(date +"%Y-%m-%d %T")
echo "[$TIMESTAMP] Job started" >> /var/log/30sec_job.log
# Your actual job commands here
STATUS=$?
echo "[$(date +"%Y-%m-%d %T")] Job completed with status $STATUS" >> /var/log/30sec_job.log

For Python implementations, consider:

import time
import subprocess

while True:
    start_time = time.time()
    # Your task logic here
    subprocess.run(['/path/to/your/command'])
    elapsed = time.time() - start_time
    sleep_time = max(0, 30 - elapsed)
    time.sleep(sleep_time)