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:

  1. 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 #####
  2. 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.

  3. 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 #####
  4. 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
  5. 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.

  6. 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.

  7. 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.

  8. 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 #####
  9. 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.

  10. 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
  11. 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 #####
  12. Generate a local request to create a new access-log entry.
    $ curl --head --silent --output /dev/null --write-out '%{http_code}\n' http://localhost/
    200

    Any request that reaches Apache (browser refresh, health check, load balancer probe) can be used for this verification.

  13. 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"