Tech Verse Logo
Enable dark mode
Mastering Logrotate Configuration

Mastering Logrotate Configuration

Md. Mostafijur RahmanMMd. Mostafijur Rahman

Md. Mostafijur Rahman

•4 min read

Unmanaged log files will eventually fill your disk. Applications write continuously, and standard system utilities need a predictable way to archive old entries without interrupting running processes. The standard tool for this task is logrotate, but its default configurations often hide subtle performance traps that bite production systems under heavy load.

You configure logrotate through flat text files typically placed inside the /etc/logrotate.d/ directory. A clean logrotate configuration file defines how often a file rotates, how many historical copies to keep, and how to handle compression. Here's a production-ready example for a custom application log:

/var/log/myapp/*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    create 0640 www-data www-data
    sharedscripts
    postrotate
        systemctl reload myapp
    endscript
}

Let's break down what each directive actually does. The daily option tells the daemon to check the log file every day. The rotate 14 directive ensures you keep two weeks of history before dropping the oldest logs. Using compress pairs well with delaycompress, which leaves the most recently rotated file uncompressed for twenty-four hours. This saves CPU cycles if your debugging tools still need to grep through yesterday's entries immediately.

The missingok flag prevents errors if a log file is absent during a rotation cycle, and notifempty skips rotation entirely if the log file size is zero bytes. Permissions matter immensely for security. The create 0640 www-data www-data line generates a fresh log file with strict read permissions immediately after moving the old one aside.

The Mechanics of Log Rotation and Process Signals

When logrotate runs, it renames the current log file, appends a number or date extension, and then creates a brand-new file with the original name. The critical gotcha here lies in how file descriptors work in Linux. When a long-running process keeps a log file open, its file descriptor points to the inode of that specific file. Simply renaming the file on disk doesn't change where the process writes its bytes. The application process will continue writing to the renamed file until it receives a signal to close and reopen its log handles.

This is why the postrotate script block is essential in the configuration shown earlier. By running systemctl reload myapp, you'll instruct the application to drop its old file descriptors and open the new log file created by logrotate. If you skip this step, your application will keep writing into the archived, compressed log file on disk until the service eventually restarts.

For developers running complex python services or managing background queues, understanding how system resources and file handles interact is just as important as handling asynchronous tasks, similar to the concepts covered when reviewing Async Python: asyncio Without the Confusion. If your application handles thousands of concurrent requests, dropping file descriptors abruptly without proper synchronization can lead to dropped log entries.

Understanding the Copytruncate Trap

Sometimes you can't signal an application to reopen its log files. Third-party daemons or legacy software might lack a reload signal handler entirely. Developers often reach for the copytruncate directive to solve this constraint, but it introduces a dangerous race condition.

The copytruncate strategy works by copying the contents of the active log file to the destination backup file, and then truncating the original log file down to zero bytes in place. Because the file is never renamed or closed, the application process keeps its file descriptor intact and continues writing without interruption.

The hidden danger is that copytruncate isn't atomic. Between the moment logrotate finishes reading the file contents and the moment it executes the truncation command, your application can write new log lines. Those intermediate log lines get completely lost during the truncation step because the truncate operation wipes the file from byte zero. If your application logs high-volume audit trails or financial transactions, using copytruncate will cause silent data loss.

If you must manage applications that refuse to close log files gracefully, consider managing their environments carefully. For isolating runtime dependencies and keeping your deployment footprint clean, standard containerization practices or environment management tools like those discussed in Python venv vs uv vs Poetry: Choosing for Production offer much tighter control over process lifecycles.

Testing Your Configuration

Never push a new logrotate configuration to production without testing it manually. The logrotate binary includes a verbose debug flag that simulates the entire rotation process without altering your actual log files or invoking post-rotation scripts.

Run the test command with your specific configuration file path:

logrotate -d /etc/logrotate.d/myapp

Examine the output carefully. The debug command prints every step the utility intends to take, showing whether files match, when rotation triggers, and what scripts execute. If you need to force an immediate rotation on a live staging server to verify permissions and compression behavior, omit the debug flag and run the force flag instead:

logrotate -f /etc/logrotate.d/myapp

Check the system status file located at /var/lib/logrotate/status afterward. This file records the exact timestamp of the last rotation for every registered log target. If logrotate refuses to rotate your files, inspect this status file first. Stale entries often block unexpected rotation attempts until the scheduled interval finally rolls over.

Md. Mostafijur RahmanMMd. Mostafijur Rahman

WRITTEN BY

Md. Mostafijur Rahman

    Latest Posts

    View All

    Automating Backups with Cron and Rsync

    Automating Backups with Cron and Rsync

    Mastering Logrotate Configuration

    Mastering Logrotate Configuration

    Systemd Services for Long-Running Processes

    Systemd Services for Long-Running Processes

    Zero-Downtime Nginx Reloads and Config Testing

    Zero-Downtime Nginx Reloads and Config Testing

    Profiling Python: Finding the Actual Bottleneck

    Profiling Python: Finding the Actual Bottleneck

    SQLAlchemy 2.0 for Eloquent Developers

    SQLAlchemy 2.0 for Eloquent Developers

    Django vs FastAPI vs Flask: Pick the Right Python Stack

    Django vs FastAPI vs Flask: Pick the Right Python Stack

    Clean Pytest: Fixtures, Parametrisation, and Mocks

    Clean Pytest: Fixtures, Parametrisation, and Mocks

    Async Python: asyncio Without the Confusion

    Async Python: asyncio Without the Confusion

    Python Type Hints and Mypy: Real World Patterns

    Python Type Hints and Mypy: Real World Patterns