High-traffic Apache servers can generate large access and error logs, and unattended growth can exhaust disk space, trigger outages, and bury incident evidence under noise.
On most Linux systems, logrotate rotates logs by renaming files on a schedule or size threshold, optionally compressing older copies to save space. Apache keeps log file descriptors open, so rotation is typically paired with a reload or graceful restart to reopen log files and continue writing to the current paths.
The examples assume file-based logging under /var/log/apache2 on Debian-style systems using /etc/logrotate.d/apache2; custom per-virtual-host paths must be included in rotation patterns to avoid unbounded growth. Avoid copytruncate for busy servers because it can drop or duplicate lines under load, and avoid double-rotation when logs are already piped through rotatelogs.
Steps to configure log rotation for Apache:
- List ErrorLog, CustomLog directives to confirm the active log paths.
$ sudo grep --recursive --line-number --extended-regexp '^[[:space:]]*(ErrorLog|CustomLog)[[:space:]]' /etc/apache2/ 2>/dev/null | head -n 12 /etc/apache2/conf-available/other-vhosts-access-log.conf:2:CustomLog ${APACHE_LOG_DIR}/other_vhosts_access.log vhost_combined /etc/apache2/apache2.conf:134:ErrorLog ${APACHE_LOG_DIR}/error.log /etc/apache2/sites-available/000-default.conf:20: ErrorLog ${APACHE_LOG_DIR}/error.log /etc/apache2/sites-available/000-default.conf:21: CustomLog ${APACHE_LOG_DIR}/access.log combined /etc/apache2/sites-available/default-ssl.conf:12: ErrorLog ${APACHE_LOG_DIR}/error.log /etc/apache2/sites-available/default-ssl.conf:13: CustomLog ${APACHE_LOG_DIR}/access.log combined ##### snipped ##### - Resolve the Debian-style ${APACHE_LOG_DIR} value used in the configuration.
$ sudo bash -c 'source /etc/apache2/envvars && echo "$APACHE_LOG_DIR"' /var/log/apache2
RHEL-style systems commonly use /var/log/httpd with /etc/logrotate.d/httpd.
- Record current log sizes to establish a baseline before changing rotation.
$ sudo ls -lh /var/log/apache2/ | head -n 20 total 4.0K -rw-r----- 1 root adm 0 Jan 10 13:32 access.log -rw-r----- 1 root adm 693 Jan 10 13:43 error.log -rw-r----- 1 root adm 0 Jan 10 13:32 other_vhosts_access.log ##### snipped #####
- Locate the logrotate policy file that applies to Apache.
$ sudo ls -la /etc/logrotate.d/apache2 -rw-r--r-- 1 root root 397 Mar 18 2024 /etc/logrotate.d/apache2
- Review the current rotation settings for log patterns, including the postrotate signal.
$ sudo sed -n '1,200p' /etc/logrotate.d/apache2 /var/log/apache2/*.log { daily missingok rotate 14 compress delaycompress notifempty create 640 root adm sharedscripts prerotate if [ -d /etc/logrotate.d/httpd-prerotate ]; then run-parts /etc/logrotate.d/httpd-prerotate fi endscript postrotate if pgrep -f ^/usr/sbin/apache2 > /dev/null; then invoke-rc.d apache2 reload 2>&1 | logger -t apache2.logrotate fi endscript }A missing reopen signal leaves Apache writing to the renamed file, which defeats rotation.
- Update the rotation policy to match retention and permissions requirements.
/var/log/apache2/*.log { daily rotate 14 compress delaycompress missingok notifempty create 640 root adm sharedscripts postrotate /usr/sbin/apachectl graceful >/dev/null 2>&1 || true endscript }Add size 100M to rotate by size, or replace daily with weekly for slower-growing logs.
Add su root adm if logrotate refuses to rotate due to directory permission safety checks.
- Add a separate logrotate stanza for any logs written outside /var/log/apache2.
/srv/www/*/logs/*.log { weekly rotate 8 compress delaycompress missingok notifempty create 640 root adm sharedscripts postrotate /usr/sbin/apachectl graceful >/dev/null 2>&1 || true endscript }Keep rotation patterns tight to avoid rotating non-Apache logs with incompatible ownership or reopen behavior.
- Run a dry run to validate the policy without changing files.
$ sudo logrotate --debug /etc/logrotate.d/apache2 warning: logrotate in debug mode does nothing except printing debug messages! Consider using verbose mode (-v) instead if this is not what you want. reading config file /etc/logrotate.d/apache2 Reading state from file: /var/lib/logrotate/status ##### snipped ##### rotating pattern: /var/log/apache2/*.log after 1 days (14 rotations) empty log files are not rotated, old logs are removed considering log /var/log/apache2/access.log log does not need rotating (log has already been rotated) ##### snipped #####
- Force a rotation to validate behavior in a controlled window.
$ sudo logrotate --force /etc/logrotate.d/apache2
Forced rotation renames files immediately, which can add I/O load and complicate troubleshooting if log names change mid-incident.
- Confirm rotated logs exist with a fresh replacement file.
$ sudo ls -lh /var/log/apache2/ | grep --extended-regexp 'access\.log|error\.log' -rw-r----- 1 root adm 0 Jan 10 13:32 access.log -rw-r----- 1 root adm 279 Jan 10 13:43 error.log -rw-r----- 1 root adm 828 Jan 10 13:43 error.log.1 -rw-r----- 1 root adm 0 Jan 10 13:32 other_vhosts_access.log
- Verify that Apache remains active after rotation.
$ sudo systemctl status apache2 --no-pager --lines=12 ● apache2.service - The Apache HTTP Server Loaded: loaded (/usr/lib/systemd/system/apache2.service; enabled; preset: enabled) Active: active (running) since Sat 2026-01-10 13:43:16 +08; 23s ago ##### snipped ##### - Generate a local request to create a new access-log entry.
$ curl --head --silent --output /dev/null --write-out '%{http_code}\n' http://localhost/ 200Any request that reaches Apache (browser refresh, health check, load balancer probe) can be used for this verification.
- Confirm the active access log receives fresh entries after rotation.
$ sudo tail -n 3 /var/log/apache2/access.log 127.0.0.1 - - [10/Jan/2026:13:43:39 +0800] "HEAD / HTTP/1.1" 200 255 "-" "curl/8.5.0"
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.
