How to Set Time Limits for Cron Jobs on Ubuntu (Prevent Long-Running Processes)


3 views

When cron jobs exceed their expected runtime, they can cause resource contention and scheduling conflicts. On Ubuntu servers (including 10.04 LTS), we have several effective approaches to enforce time constraints.

The simplest solution is wrapping your command with GNU's timeout utility. This comes pre-installed on most Ubuntu systems:

# Basic syntax (kills after 1 hour)
0 * * * * /usr/bin/timeout 3600 /path/to/your/script.sh

For better logging:

0 * * * * /usr/bin/timeout -k 300 3600 /path/to/script.sh > /var/log/script.log 2>&1

This gives the process 5 minutes (300s) to clean up after the initial SIGTERM before sending SIGKILL.

For more control, implement timeout logic directly in your script:

#!/bin/bash
TIMEOUT=3600
START_TIME=$(date +%s)

# Your actual job logic here
process_data_files() {
    # Long-running operations
}

# Launch in background
process_data_files &

# Monitor runtime
while true; do
    CURRENT_TIME=$(date +%s)
    ELAPSED=$((CURRENT_TIME - START_TIME))
    
    if [ $ELAPSED -ge $TIMEOUT ]; then
        echo "Timeout reached! Killing process..."
        kill %1
        exit 1
    fi
    
    # Check if background job completed
    jobs | grep -q 'Done' && break
    
    sleep 30
done

If you're on newer Ubuntu versions with systemd:

[Unit]
Description=Hourly Processing Job
RuntimeMaxSec=3600

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

[Install]
WantedBy=timers.target

Create a companion timer unit to schedule execution.

For complex scenarios, use cgroups to limit resources and runtime:

# Create cgroup
sudo cgcreate -g cpu,memory:/limited_job

# Set limits (1 CPU hour equivalent)
sudo cgset -r cpu.cfs_period_us=1000000 -r cpu.cfs_quota_us=3600000000 limited_job
sudo cgset -r memory.limit_in_bytes=2G limited_job

# Execute cron job within cgroup
0 * * * * cgexec -g cpu,memory:limited_job /path/to/script.sh

Always implement proper logging for timeout events:

#!/bin/bash
TIMEOUT=3500 # 50 minute warning threshold
LOG_FILE="/var/log/cron_timeout.log"

{
    /usr/bin/timeout 3600 /path/to/script.sh || 
    echo "$(date): Job timed out after 3600 seconds" >> $LOG_FILE
} 2>&1 | logger -t hourly_job

When implementing time limits:

  • Child processes may not terminate - use timeout --foreground
  • Cleanup operations might be interrupted - implement signal traps
  • System clock changes can affect timing - use CLOCK_MONOTONIC in scripts

When managing server operations, cron jobs are essential for scheduling tasks. However, sometimes these jobs can run longer than expected, causing resource contention or delaying subsequent executions. On Ubuntu servers (including 10.04 as mentioned), we need reliable ways to enforce runtime limits.

Here are three effective approaches to control cron job execution time:


# Method 1: Using the timeout command (recommended)
0 * * * * /usr/bin/timeout 300 /path/to/your/script.sh

The timeout command (part of GNU Coreutils) will automatically terminate the process after the specified time (300 seconds in this example).


# Method 2: Implementing timers within the script itself
#!/bin/bash
START_TIME=$(date +%s)
MAX_DURATION=300 # 5 minutes

your_actual_commands_here

# Check elapsed time periodically
CURRENT_TIME=$(date +%s)
ELAPSED_TIME=$((CURRENT_TIME - START_TIME))
if [ $ELAPSED_TIME -gt $MAX_DURATION ]; then
    echo "Timeout reached!" >&2
    exit 1
fi

For more complex scenarios, consider these methods:


# Method 3: Using ulimit (resource limiting)
0 * * * * ulimit -t 300; /path/to/script.sh

# Method 4: Using systemd-run (on newer systems)
0 * * * * systemd-run --scope --property=RuntimeMaxSec=300 /path/to/script.sh

Always implement proper logging to track timeouts:


#!/bin/bash
{
    timeout 300 /path/to/script.sh || 
    echo "$(date): Script timed out after 5 minutes" >> /var/log/cron_timeouts.log
} 2>&1 | logger -t cron_job

For maximum control, create a timeout wrapper:


#!/bin/bash
# timeout_wrapper.sh
TIMEOUT=$1
shift
CMD="$@"

# Start the command
$CMD &
CMD_PID=$!

# Start timeout counter
(
    sleep $TIMEOUT
    if kill -0 $CMD_PID 2>/dev/null; then
        kill -TERM $CMD_PID
    fi
) &
TIMEOUT_PID=$!

# Wait for command to complete
wait $CMD_PID 2>/dev/null
STATUS=$?

# Clean up timeout process
kill $TIMEOUT_PID 2>/dev/null

exit $STATUS

Then call it from cron:


0 * * * * /path/to/timeout_wrapper.sh 300 /path/to/your/script.sh