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