Nginx writes a line to its access log for every request and a line to its error log for every warning or failure, and PHP-FPM does the same for slow requests and fatal errors. None of it gets cleaned up automatically. On a busy site, an access log alone can grow by tens of megabytes a week, and on a VPS with a fixed disk allocation rather than elastic storage, an unrotated log directory is a genuinely common way for a server to run out of space months after it was set up, with nothing else about the site having changed.
This guide configures logrotate, the tool most Ubuntu services already rely on, to compress, rotate, and eventually delete Nginx and PHP-FPM logs on a fixed schedule automatically. It assumes you have Nginx and PHP-FPM already running on the VPS, for example from the [WordPress deployment guide](/guides/deploy-wordpress-nginx-php-fpm).
logrotate ships with Ubuntu by default and already has a configuration file for Nginx, installed automatically alongside the Nginx package. Check it before writing anything new:
cat /etc/logrotate.d/nginx
This default config rotates Nginx logs, but the defaults are conservative and worth tuning for a small VPS specifically: they are not always sized for a server where disk space is a fixed, purchased resource rather than something you can grow on demand. PHP-FPM, unlike Nginx, does not ship a logrotate config by default, so that part needs to be written from scratch.
Before writing rotation rules, confirm the actual log paths, since a logrotate rule pointed at the wrong file silently does nothing. For Nginx, check the log directives in the active server block:
grep -r 'access_log\|error_log' /etc/nginx/sites-enabled/
This normally resolves to /var/log/nginx/access.log and /var/log/nginx/error.log unless a site-specific path was set. For PHP-FPM, check the pool configuration:
grep 'php_admin_value\[error_log\]\|slowlog' /etc/php/*/fpm/pool.d/www.conf
The default PHP-FPM error log is typically /var/log/php8.3-fpm.log, though the exact filename depends on the PHP version installed. Note the paths returned here; they are what the logrotate config in the next step needs to point at exactly.
Edit the default config directly rather than creating a second file alongside it. logrotate does not merge or prioritise between multiple stanzas that target the same log paths; it processes every file it finds under /etc/logrotate.d/, so a separate config pointed at the same logs as the default one causes duplicate rotation attempts and "log has already been rotated" errors. The default file is safe to edit directly; a future Nginx package upgrade does not silently overwrite a config file you have already modified.
sudo nano /etc/logrotate.d/nginx
Replace its contents entirely with the following, adjusting the path if your logs live somewhere other than the default:
/var/log/nginx/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
create 0640 www-data adm
sharedscripts
postrotate
systemctl reload nginx > /dev/null 2>&1 || true
endscript
}
daily rotates the log every day rather than waiting for it to grow to a fixed size, keeping each individual file small and easy to search. rotate 14 keeps two weeks of history before deleting the oldest file, which is normally enough to investigate an issue without keeping logs indefinitely. compress and delaycompress gzip old logs to save space, but skip compressing the most recent rotated file in case something is still actively reading it. The postrotate block reloads Nginx after rotation so it starts writing to the new empty file immediately instead of continuing to write into the now-renamed old one.
PHP-FPM has no default logrotate config, so this file needs to be created rather than adjusted. Use the log path confirmed in step 2:
sudo nano /etc/logrotate.d/php-fpm
/var/log/php8.3-fpm.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
create 0640 www-data adm
sharedscripts
postrotate
systemctl reload php8.3-fpm > /dev/null 2>&1 || true
endscript
}
Replace php8.3-fpm with the correct version if the server is running a different PHP release; check with php -v if unsure. The structure mirrors the Nginx config directly, with the postrotate step reloading PHP-FPM instead of Nginx so it releases the old log file handle and starts writing to the fresh one. Since this file did not exist before, there is no conflicting default to worry about here.
logrotate runs automatically once a day via a system cron job, but waiting 24 hours to find out whether the configuration actually works is not a productive use of time. Force a dry run to see what would happen without changing anything:
sudo logrotate -d /etc/logrotate.d/nginx
This prints exactly what logrotate would do, including which files it considers eligible for rotation, without actually rotating them. Once the output looks correct, force a real rotation to confirm the postrotate script runs cleanly and the service reloads without error:
sudo logrotate -f /etc/logrotate.d/nginx
Check that a new compressed file appeared in /var/log/nginx/ and that Nginx is still serving requests normally afterward. Repeat both commands against /etc/logrotate.d/php-fpm to confirm it as well.
Nginx and PHP-FPM logs now rotate daily, compress after a day's delay, and get deleted automatically after two weeks, so log growth is no longer something that can quietly fill the disk months down the line. This pairs directly with the [server health and disk space monitoring guide](/blog/monitor-server-health-disk-space-vps), which catches any other source of disk growth that rotation alone will not, database bloat, old backups, or an uploads folder from a since-deleted site.
Move a live WordPress site from shared hosting to a VPS: database export with my...
How to set up SSH key authentication on Ubuntu and disable password logins to se...
Install WordPress on a Linux VPS with Nginx, PHP-FPM, and MariaDB. Complete step...