High-volume web servers generate gigabytes of log data every single day. If you don't manage this growth, your root partition will eventually fill up and halt your application entirely. Setting up log rotation correctly requires more than dropping a generic file into your system directories. You need to understand how daemons write to disk, how file descriptors work, and how to avoid dropping active request streams during the rotation cycle.
Many system administrators configure logrotate with default settings that work fine for minor personal scripts, but fail catastrophically under heavy traffic loads. When a high-throughput application writes to an access log, simply moving the file causes the web server process to keep writing to the old, unlinked file descriptor until the process reloads. Worse, if your rotation script truncates the file instead of copying and truncating it atomically, you'll lose valuable audit lines or trigger partial writes that corrupt log parsers. It's a common oversight that leads to silent data loss during peak traffic hours.
Writing a Safe Rotation Block
To configure logrotate web server targets properly, you should place a dedicated configuration file inside the system directory. Avoid editing global configuration files directly. Create a file for your application stack inside the primary configuration path. Here's a production-ready configuration file that targets a high-volume application writing logs to standard paths.
/var/www/html/storage/logs/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 www-data www-data
sharedscripts
postrotate
systemctl reload php8.5-fpm > /dev/null 2>&1 || true
endscript
}Let's look at why specific directives matter here. The delaycompress option is non-negotiable for high-throughput applications. If you compress logs immediately on the first day, the logging daemon might still hold an open file descriptor to the uncompressed file and continue writing to it while it's being gzipped. Delaying compression until the subsequent rotation cycle avoids this race condition entirely. The sharedscripts directive ensures that the postrotate block executes only once for the entire group of matched log files, rather than triggering a service reload for every single log file matched by the wildcard.
File ownership and permissions deserve close attention too. The create directive specifies the mode, owner, and group for the newly created log files after rotation occurs. If these permissions don't match what your application expects, the web server process won't be able to write to the new file descriptors after rotation completes. You'll end up with silent application errors in your error logs, which won't show up until someone checks the application manually. It's a cascading failure pattern that's entirely avoidable with correct configuration.
Handling Web Server Daemons and Reloads
Web servers like Nginx and Apache cache open file descriptors for performance reasons. When logrotate moves an access log to a backup name, the web server doesn't automatically realize the file moved. It keeps writing to the inode of the original file, which now lives in unlinked storage space. You don't get an error, but you also don't see new logs appearing in the expected filesystem path. Disk space doesn't actually free up until you force the daemon to close and reopen its log files.
You have two primary ways to handle this within logrotate. You can use the copytruncate directive, or you can trigger a graceful service reload via postrotate scripts. Avoid copytruncate on high-volume systems. It copies the file and then truncates the original in place. During the copy operation, high write volume can cause data to be lost if lines are written between the read and the truncate steps. Furthermore, copying gigabytes of log data every single night creates unnecessary disk I/O load. Instead, use graceful reloads that send specific signals to the daemon.
http {
access_log /var/log/nginx/access.log combined;
error_log /var/log/nginx/error.log warn;
}
When logrotate runs, it renames the logs, creates new empty files with the correct permissions, and then runs the commands defined in your script block. For Nginx, you want to send a user-defined signal that instructs the master process to reopen log files without dropping active client connections. You should read about zero-downtime Nginx reloads to understand how signals propagate through worker processes. The standard command for this operation involves invoking the init system or sending the signal directly to the master process identifier.
Here is what the matching logrotate block for an Nginx installation looks like in production environments. It targets the web server logs specifically while avoiding interference with application-level logs handled elsewhere.
/var/log/nginx/*.log {
daily
missingok
rotate 30
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
postrotate
if [ -d /run/systemd/system ]; then
systemctl reload nginx > /dev/null 2>&1 || true
fi
endscript
}Notice the conditional check for systemd inside the postrotate block. While modern Linux distributions universally rely on systemd, automation scripts sometimes run in containerized environments or minimal base images where systemctl isn't available or behaves differently. Guarding the reload command prevents the entire logrotate cron job from throwing fatal exit codes and failing silently. If logrotate exits with a non-zero status code, subsequent rotations for other services might be skipped entirely.
Testing Configurations Before Production Deployment
Never push a new logrotate configuration directly to a production machine without testing it first. A syntax error in your configuration file or a misplaced brace will cause the cron job to fail across the entire system. Logrotate provides a built-in debug flag that lets you simulate the rotation cycle without actually modifying any files or touching disk blocks. It's your primary safety net.
Run your configuration file through the debugging command manually from your terminal. You'll see verbose output detailing exactly which files matched your patterns, what actions would be taken, and whether any configuration directives conflict with each other.
logrotate -d /etc/logrotate.d/webappIf you need to force a test run that actually executes the rotation immediately—useful for verifying that file permissions and postrotate scripts work correctly—use the force flag combined with the verbose flag. Watch your system logs during this process to catch any permission denied errors or failed reloads instantly.
logrotate -vf /etc/logrotate.d/webappDisk space issues on production servers usually stem from overlooked log files accumulating in obscure directories. Pair your log rotation setup with proper disk monitoring tools so you receive alerts long before partitions reach critical thresholds. It's much easier to tweak a rotation retention setting than it is to recover a crashed database instance running on a fully exhausted root disk partition.












