
Web logs grow quietly—until they don’t. One noisy bot, a PHP warning loop, or a misconfigured access log can chew through tens of gigabytes overnight. When that happens, a full disk can take your VPS down.
This logrotate tutorial walks you through a hosting-safe setup for Nginx, Apache, and PHP logs. You’ll get predictable retention, compression, and clean reloads.
The goal isn’t “rotate occasionally.” You want clear disk limits and enough history to troubleshoot. You also want rotations that don’t break services or drop log lines.
What you’ll build in this log rotation setup
- Daily rotation for busy logs, weekly for quieter ones
- Compression with a delay (so today’s logs stay readable)
- Safe truncation or reload (depending on daemon behavior)
- Retention rules that match hosting realities (7–30 days)
- Quick verification steps and a rollback plan
These steps apply to Ubuntu 24.04/26.04, Debian 12/13, AlmaLinux 9/10, and Rocky Linux 9/10. Paths vary by distro, but the approach stays the same.
If you want a VPS where you can enforce rotation policies (instead of hoping distro defaults behave), start with a HostMyCode VPS. You get root access, predictable storage, and room to tune logging without shared-host limits.
Prerequisites and safety checks (5 minutes)
Before you change anything, check disk headroom. Logrotate won’t rescue a server at 99% usage. Compression can also need brief extra space.
df -h
sudo du -xh /var/log --max-depth=1 | sort -h
sudo du -xh /var/log/nginx /var/log/apache2 2>/dev/null | sort -h
On cPanel servers, you’ll often see large trees under /usr/local/apache/logs. You may also find per-domain logs under /home/*/logs, depending on the setup.
This guide targets OS-level stacks (Nginx/Apache/PHP). These are common on VPS and dedicated servers without WHM.
Understand how logrotate runs on your distro
Most distros run logrotate daily via a systemd timer or cron. Identify which one you use so your tests match production.
Ubuntu/Debian (systemd timer is common)
systemctl list-timers | grep -i logrotate
systemctl status logrotate.timer
AlmaLinux/Rocky (often cron.daily)
ls -l /etc/cron.daily/logrotate
rpm -q logrotate
In both cases, the global config is typically /etc/logrotate.conf. Per-service rules usually live in /etc/logrotate.d/.
Set sane global defaults (and keep them boring)
Edit /etc/logrotate.conf and keep it simple. Put special behavior in per-service files. They’re easier to review and safer to change.
sudo nano /etc/logrotate.conf
A practical baseline for a hosting VPS:
# /etc/logrotate.conf
weekly
rotate 4
create
compress
delaycompress
missingok
notifempty
su root adm
# Packages install per-service rules here
include /etc/logrotate.d
- weekly + rotate 4 gives you a predictable “about a month” of low-volume logs.
- compress + delaycompress keeps the newest rotated file readable while older history gets compressed.
- su root adm avoids permission failures on Debian/Ubuntu, where many logs are
root:adm.
Keep the global policy conservative. Use daily and size caps only for logs that actually need them.
Rotate Nginx logs without losing lines
Nginx can reopen log files cleanly. That’s usually better than copytruncate on busy sites. It avoids the small window where writes can land in the wrong place during truncation.
Create or edit /etc/logrotate.d/nginx:
sudo nano /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
rotate 14
missingok
notifempty
compress
delaycompress
dateext
dateformat -%Y%m%d
sharedscripts
postrotate
[ -s /run/nginx.pid ] && kill -USR1 `cat /run/nginx.pid`
endscript
}
A few details here pay off later:
- daily + rotate 14 fits hosting well: enough history without hoarding.
- dateext makes timelines obvious (“what happened on 2026-09-11?”) and reduces restore confusion.
- kill -USR1 tells Nginx to reopen its log files immediately.
If your Nginx uses a different PID path, confirm it first:
ps -eo pid,cmd | grep [n]ginx
sudo nginx -T | grep -i pid
Rotate Apache logs safely (and handle both Debian and RHEL paths)
Apache log locations depend on the distro:
- Debian/Ubuntu:
/var/log/apache2/*.log - AlmaLinux/Rocky:
/var/log/httpd/*log
Use the rule that matches your server. Keeping one file per platform is usually clearer than forcing a “universal” rule.
Debian/Ubuntu Apache logrotate rule
sudo nano /etc/logrotate.d/apache2
/var/log/apache2/*.log {
daily
rotate 14
missingok
notifempty
compress
delaycompress
dateext
sharedscripts
postrotate
/usr/sbin/apachectl graceful > /dev/null 2>/dev/null || true
endscript
}
AlmaLinux/Rocky Apache logrotate rule
sudo nano /etc/logrotate.d/httpd
/var/log/httpd/*log {
daily
rotate 14
missingok
notifempty
compress
delaycompress
dateext
sharedscripts
postrotate
/bin/systemctl reload httpd.service > /dev/null 2>/dev/null || true
endscript
}
If you run Apache behind Nginx (common in mixed PHP stacks), Apache logs can still grow quickly. Backend traffic stays high even when Nginx serves static files.
If you’re building that layout, this HostMyCode guide is a solid companion: Reverse proxy setup tutorial.
Rotate PHP logs (PHP-FPM, PHP errors, and per-pool files)
PHP logging varies because it depends on your PHP-FPM and app configuration. On a typical VPS you might see:
- PHP-FPM master log:
/var/log/php8.3-fpm.log(path varies) - Per-pool slowlog:
/var/log/php-fpm/www-slow.log - App-level PHP error logs: often under
/var/log/or inside the site tree (usually a bad idea)
Start by inventorying what’s on disk:
sudo find /var/log -maxdepth 2 -type f \( -name '*php*' -o -name '*fpm*' \) -print
Example rule for PHP-FPM logs (systemd reload)
Create /etc/logrotate.d/php-fpm:
sudo nano /etc/logrotate.d/php-fpm
/var/log/php*-fpm*.log /var/log/php-fpm/*.log {
weekly
rotate 8
missingok
notifempty
compress
delaycompress
dateext
sharedscripts
postrotate
systemctl reload php8.3-fpm.service > /dev/null 2>/dev/null || true
systemctl reload php8.2-fpm.service > /dev/null 2>/dev/null || true
systemctl reload php-fpm.service > /dev/null 2>/dev/null || true
endscript
}
The multiple reload lines are intentional. Across a fleet, PHP versions differ. This “try and continue” pattern keeps one missing unit from breaking the entire rotation run.
If you’re running PHP-FPM on a hosting VPS and want to tune performance too, pair this with: PHP-FPM setup guide tutorial.
Add a size-based safety net for sudden log storms
Daily rotation helps, but a log storm can fill a disk in hours. For the riskiest files, add a size threshold so they rotate early.
Example for Nginx access logs: rotate once the file hits 200MB, even if the daily schedule hasn’t rolled over yet.
/var/log/nginx/access.log {
daily
size 200M
rotate 14
missingok
notifempty
compress
delaycompress
dateext
sharedscripts
postrotate
[ -s /run/nginx.pid ] && kill -USR1 `cat /run/nginx.pid`
endscript
}
Important: size-based rotation is only evaluated when logrotate runs. If logrotate runs once per day, size 200M is checked once per day.
If you need more frequent checks, add an additional timer/cron (outside this tutorial). Or reduce log volume at the source (rate limiting, WAF, bot rules).
On WordPress sites, rate limiting often cuts log volume dramatically during brute-force waves. See: Nginx rate limiting tutorial.
Handle app logs in site directories (and why it’s risky)
A common VPS failure mode: an app writes logs under /var/www/site/storage/logs or wp-content/debug.log. Logrotate won’t manage those by default. They can grow unchecked.
If you must rotate logs inside a site directory, add an explicit rule. Keep permissions tight.
sudo nano /etc/logrotate.d/site-app-logs
/var/www/example.com/current/storage/logs/*.log {
daily
rotate 10
missingok
notifempty
compress
delaycompress
copytruncate
}
Why copytruncate here? Many apps keep file handles open. They also don’t reopen logs on a signal. In that case, copytruncate is the practical option.
The tradeoff is a small chance you lose a few lines during rotation.
Test your rules safely (dry run, then forced run)
Test before you trust. One broken rule can stop rotation across the server.
1) Dry run (recommended first step)
sudo logrotate -d /etc/logrotate.conf
Focus on three things:
- Any “error: …” output (fix this first)
- Which logs logrotate plans to rotate
- Which postrotate commands it will run
2) Force one run (use during maintenance window)
sudo logrotate -f /etc/logrotate.conf
Then confirm you got new rotated files. Also confirm services still write to the active logs:
sudo ls -lh /var/log/nginx | head
sudo tail -n 20 /var/log/nginx/error.log
sudo systemctl status nginx --no-pager
Verify retention and disk impact (your quick checklist)
- Retention: count rotated files and confirm older ones disappear after the expected number of runs.
- Compression: verify you see
.gzafter the delay period. - Permissions: make sure your log readers (often
adm) can still read logs. - Reload safety: confirm reload/graceful actions didn’t fail and didn’t cause avoidable disruption.
# How big are the logs now?
sudo du -sh /var/log/nginx /var/log/apache2 2>/dev/null
# Are there old logs you expected to be gone?
sudo ls -1 /var/log/nginx | grep -E 'access|error' | tail -n 30
Troubleshooting: common logrotate failures on hosting servers
Problem: “permission denied” during rotation
Add an explicit su line in the rule that’s failing. On Debian/Ubuntu, this often fixes logs owned by root:adm.
/var/log/nginx/*.log {
su root adm
...
}
Problem: rotated logs stop updating (service still running)
This usually means the daemon kept writing to the old file handle. Use the right reopen/reload action:
- Nginx: USR1
- Apache: reload/graceful
- PHP-FPM: reload
Quick confirmation: if lsof shows the service writing to a “(deleted)” file, that’s the issue.
sudo lsof | grep '/var/log/nginx' | grep deleted
Problem: logs rotate too often, too many files
Lower rotate or move low-volume logs back to weekly rotation. For multi-GB access logs, keep daily rotation. Keep compression on, and shorten retention.
Problem: disk still fills up even with rotation
Rotation limits history. It does not reduce log generation. If the server is under attack or misconfigured, fix the cause:
- Block brute-force and bots (rate limiting, firewall rules)
- Fix PHP warning loops and plugin errors
- Disable overly verbose debug logging in production
If you suspect a broader security issue, check the basics (SSH exposure, risky services, open mail relays). This checklist fits well alongside log cleanup work: VPS security audit tutorial.
Optional: keep logs useful for incident response
Short retention feels fine until you need to answer “when did this start?” Keep enough history to spot patterns. Avoid stockpiling junk.
- Access logs: 7–14 days is usually enough on small-to-mid VPS.
- Error logs: 14–30 days helps with slow-burn bugs.
- Security/auth logs: 30+ days is often worth it if you have the disk.
If you run high-traffic workloads and want consistent IO under pressure, move from shared hosting to a VPS or dedicated server. That gives you control over disk and logging policy.
HostMyCode offers managed VPS hosting if you want guardrails without owning every midnight alert.
Summary: the hosting-safe logrotate routine you can keep for years
- Set calm global defaults in
/etc/logrotate.conf. - Use service-aware postrotate actions (Nginx reopen, Apache reload, PHP-FPM reload).
- Rotate noisy logs daily with reasonable retention, and compress with delay.
- Add a size threshold for “log storm” protection where it makes sense.
- Test with
logrotate -dand validate withlsofand service status.
If you’re running this in production and want predictable performance, storage, and root access for proper log hygiene, start with a HostMyCode VPS. If you’d rather not maintain rotation rules and service reloads yourself, managed VPS hosting is the lower-maintenance option.
If your VPS has ever flipped read-only because /var filled up, log rotation is the simplest permanent fix. HostMyCode gives you a clean base to implement it: a HostMyCode VPS for full control, or managed VPS hosting if you want us to handle the ongoing tuning.
FAQ
Should I use copytruncate for Nginx and Apache?
Prefer a reopen/reload. Use copytruncate only when the process can’t reopen logs, or for app logs with no signal handling.
What retention should I use on a small hosting VPS?
A common baseline is 14 days for access/error logs, and 30 days for auth/security logs if disk allows it. Adjust based on traffic and compliance needs.
Why are my rotated logs not compressing?
If you use delaycompress, the most recent rotated file stays uncompressed until the next rotation. That’s expected.
How do I confirm a service is writing to a deleted log file?
Run sudo lsof | grep deleted. If you see Nginx/Apache holding a deleted log, reload/reopen logs and fix your postrotate action.
Can logrotate help with WordPress debug.log?
Yes, if you explicitly add a rule for that path. Still, the better fix is disabling debug logging on production once you’re done troubleshooting.