← Back to Blog
Code Snippets August 6, 2026

Bash: Log Rotation with Compression, Size Triggers & SIGUSR1 Reload

💻 Code Snippet of the Day — August 6, 2026

Bash: Log Rotation with Compression, Size Triggers & SIGUSR1 Reload

logrotate runs on a schedule and doesn't signal your app to reopen its log handle. This script does size-based rotation with zero lost lines.

#!/usr/bin/env bash
set -euo pipefail
MAX_SIZE_MB=100; KEEP=5; COMPRESS="gzip"
APP_PID_FILE=""; WATCH_MODE=false

while [[ $# -gt 0 ]]; do
    case "$1" in
        --max-size) MAX_SIZE_MB="$2"; shift 2 ;;
        --keep) KEEP="$2"; shift 2 ;;
        --compress) COMPRESS="$2"; shift 2 ;;
        --app-pid) APP_PID_FILE="$2"; shift 2 ;;
        --watch) WATCH_MODE=true; shift ;;
        *) LOG_FILES+=("$1"); shift ;;
    esac
done

rotate_one() {
    local f="$1"
    [[ -f "$f" ]] || return 0
    local mb=$(( $(stat -c '%s' "$f") / 1048576 ))
    [[ "$mb" -lt "$MAX_SIZE_MB" ]] && return 0
    local ts=$(date +%Y%m%d-%H%M%S)
    cp "$f" "${f}.${ts}"; : > "$f"
    [[ -n "$APP_PID_FILE" && -f "$APP_PID_FILE" ]] && \
        kill -USR1 "$(cat "$APP_PID_FILE")" 2>/dev/null
    [[ "$COMPRESS" == "gzip" ]] && gzip -9 "${f}.${ts}"
}

if [[ "$WATCH_MODE" == true ]]; then
    while true; do for f in "${LOG_FILES[@]}"; do rotate_one "$f"; done; sleep 30; done
else
    for f in "${LOG_FILES[@]}"; do rotate_one "$f"; done
fi


Usage:
./rotate_logs.sh --max-size 100 --keep 5 --compress gzip /var/log/app/server.log
./rotate_logs.sh --max-size 500 --app-pid /var/run/app.pid /var/log/app/*.log
./rotate_logs.sh --watch --max-size 256 /var/log/app/*.log &


💡 Copy+truncate instead of mv keeps the inode alive. SIGUSR1 tells the app to reopen — zero lost lines.