Back to tutorials
Tutorial

Linux VPS log rotation setup: prevent disk full outages with logrotate + systemd (2026 tutorial)

Linux VPS log rotation setup for Nginx, PHP-FPM, and system logs using logrotate + systemd to prevent disk-full outages in 2026.

By Anurag Singh
Updated on Apr 23, 2026
Category: Tutorial
Share article
Linux VPS log rotation setup: prevent disk full outages with logrotate + systemd (2026 tutorial)

Most “disk full” incidents on a VPS aren’t caused by your database. They come from logs that never rotate: a noisy PHP app, a busy Nginx access log, or a debug flag left on all weekend. This Linux VPS log rotation setup tutorial shows how to harden logrotate on Ubuntu 22.04/24.04 and AlmaLinux/Rocky 9/10. The goal is bounded logs, reliable compression, and clean log file reopen behavior.

You’ll define a rotation policy for common hosting stacks (Nginx, Apache, PHP-FPM, MySQL/MariaDB). You’ll also add guardrails to prevent permission breakage. Finally, you’ll verify everything with dry runs and forced rotations.

The outcome should be boring: predictable disk usage and fewer 3am surprises.

What you’ll set up (and what you’ll avoid)

  • Rotate & compress logs on schedule (daily/weekly) with sensible retention.
  • Keep permissions intact using su and create.
  • Reload Nginx/Apache/PHP-FPM cleanly after rotation so they keep writing to the new file.
  • Prevent “logrotate ran, but logs still grew” failures by validating postrotate hooks.
  • Avoid monitoring-agent talk and vendor tooling; this stays focused on rotation and disk safety.

Prerequisites: a VPS where you can reproduce the problem safely

You need root (or sudo) and a server that writes logs locally. If you’re provisioning a new host, pick a VPS sized for your traffic and logging volume.

For predictable I/O and enough headroom for retention, a HostMyCode VPS with NVMe storage makes rotation and compression noticeably faster under load.

Useful baseline checks:

  • df -hT (disk usage and filesystem type)
  • sudo du -xh /var/log | sort -h | tail -n 30 (largest log trees)
  • sudo journalctl --disk-usage (systemd journal footprint)

If you’re already firefighting low disk, reduce pressure first. Truncate the worst offender only after you confirm you don’t need that file for forensics.

sudo du -sh /var/log/* | sort -h
sudo truncate -s 0 /var/log/nginx/access.log

Then come back and fix the root cause with rotation.

Step 1 — Confirm logrotate is installed and how it runs on your OS

On Ubuntu/Debian, logrotate usually runs via a systemd timer. Older builds may still use cron.

On AlmaLinux/Rocky, it’s typically triggered by /etc/cron.daily. Some images also ship a systemd timer.

Ubuntu 22.04/24.04

sudo apt-get update
sudo apt-get install -y logrotate
systemctl list-timers --all | grep -i logrotate || true
systemctl status logrotate.service logrotate.timer

AlmaLinux/Rocky 9/10

sudo dnf install -y logrotate
rpm -q logrotate
ls -l /etc/cron.daily/logrotate || true
systemctl list-timers --all | grep -i logrotate || true

Either trigger method is fine. What matters is consistency.

Logrotate must run regularly, and your rules must match how your services write logs.

Step 2 — Review your global defaults (and fix the two settings that bite most admins)

Start with the main config:

  • Debian/Ubuntu: /etc/logrotate.conf and includes from /etc/logrotate.d/
  • AlmaLinux/Rocky: same layout, with slightly different distro defaults
sudo sed -n '1,200p' /etc/logrotate.conf

Two defaults cause most “it ran but didn’t work” situations:

  • Missing su: many rotations need su root adm (or similar) to handle group-owned logs safely.
  • Wrong create owner/mode: recreating the file with the wrong user/group breaks writes and can leave you blind.

On Ubuntu, the adm group is common for log access. Check what your key logs actually look like:

ls -l /var/log/nginx/access.log /var/log/syslog 2>/dev/null || true

Resist the urge to “fix everything” in global defaults. Use per-service files under /etc/logrotate.d/.

That keeps owners and modes explicit.

Step 3 — Implement a safe rotation policy for Nginx (with verification)

On busy sites, Nginx access logs grow fast. A good baseline is daily rotation, two weeks of retention, and delayed compression.

Delayed compression keeps yesterday’s log easy to grep.

You also need a reload after rotation. Without it, Nginx can keep writing to an old file handle.

Create or edit /etc/logrotate.d/nginx:

sudo editor /etc/logrotate.d/nginx

Use a policy like this (works well on Ubuntu/Alma/Rocky with systemd):

/var/log/nginx/*.log {
  daily
  rotate 14
  missingok
  notifempty
  compress
  delaycompress
  dateext
  dateformat -%Y%m%d
  sharedscripts
  su root adm
  create 0640 www-data adm
  postrotate
    /usr/sbin/nginx -t && systemctl reload nginx >/dev/null 2>&1 || true
  endscript
}

Adjust the user to match your distro and service account:

  • Ubuntu often runs Nginx as www-data.
  • AlmaLinux/Rocky commonly use nginx. Confirm: ps -o user,group,cmd -C nginx

If your Nginx logs are owned by nginx:nginx, update:

create 0640 nginx nginx
su root nginx

Verify Nginx rotation without waiting a day

sudo logrotate -d /etc/logrotate.d/nginx
sudo logrotate -vf /etc/logrotate.d/nginx
ls -lh /var/log/nginx | head
sudo systemctl status nginx --no-pager

A classic failure mode is subtle. Rotation happens, but Nginx keeps writing to the old file because the reload didn’t run.

In that case, the “rotated” file keeps growing.

Confirm which file is still open:

sudo lsof | grep '/var/log/nginx/access.log' || true

Step 4 — Rotate PHP-FPM logs (and avoid permission regressions)

PHP-FPM logging varies by distro and packaging. You might see:

  • /var/log/php8.3-fpm.log (Debian/Ubuntu packaging varies)
  • pool-specific logs configured in /etc/php/8.3/fpm/pool.d/www.conf

First, confirm what’s actually in use:

sudo systemctl status php8.3-fpm --no-pager | sed -n '1,120p'
sudo grep -R "^error_log" -n /etc/php/8.*/fpm/pool.d 2>/dev/null || true

Create /etc/logrotate.d/php-fpm:

/var/log/php*-fpm.log {
  weekly
  rotate 8
  missingok
  notifempty
  compress
  delaycompress
  su root adm
  create 0640 root adm
  postrotate
    systemctl reload php8.3-fpm >/dev/null 2>&1 || true
    systemctl reload php8.2-fpm >/dev/null 2>&1 || true
  endscript
}

If you only run one PHP-FPM version, keep the single matching reload line.

The || true guards keep rotation from failing when a service isn’t installed.

Step 5 — Apache logs: copytruncate vs reload (choose intentionally)

With Apache, you can rotate by reloading the service (preferred). Or you can use copytruncate (simpler, but it can drop lines during heavy writes).

If you can reload safely, do that.

Edit /etc/logrotate.d/apache2 (Ubuntu) or /etc/logrotate.d/httpd (Alma/Rocky). A sensible pattern:

/var/log/apache2/*.log {
  daily
  rotate 14
  missingok
  notifempty
  compress
  delaycompress
  dateext
  sharedscripts
  su root adm
  postrotate
    /usr/sbin/apachectl configtest && systemctl reload apache2 >/dev/null 2>&1 || true
  endscript
}

On AlmaLinux/Rocky, replace with:

systemctl reload httpd

If you’re running a control panel, tread carefully. cPanel/WHM and Plesk may rewrite web server configs.

Logrotate tweaks are usually fine, but write down what you changed. That makes re-applying changes after updates painless.

If you’re still choosing a panel for a new server, best VPS control panels for Linux hosting in 2026 is a helpful overview of the operational trade-offs.

Step 6 — MySQL/MariaDB logs: rotate slowly, keep audit value

Database logs aren’t always large. Slow query logs can still explode during an incident.

Depending on your setup, MySQL/MariaDB may write to:

  • /var/log/mysql/error.log (Debian/Ubuntu)
  • /var/log/mysqld.log or journald only (Alma/Rocky varies)

Confirm the real paths on your host:

sudo mysql -e "SHOW VARIABLES LIKE 'log_error';" 2>/dev/null || true
sudo mysql -e "SHOW VARIABLES LIKE 'slow_query_log_file';" 2>/dev/null || true
sudo systemctl status mysql mariadb --no-pager | sed -n '1,120p'

If you have file-based logs, create /etc/logrotate.d/mysql-custom (the filename is up to you):

/var/log/mysql/*.log {
  weekly
  rotate 12
  missingok
  notifempty
  compress
  delaycompress
  su root adm
  create 0640 mysql adm
  sharedscripts
  postrotate
    # Ask MySQL to reopen log files
    /usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf flush-logs 2>/dev/null || true
  endscript
}

On AlmaLinux/Rocky, you may not have /etc/mysql/debian.cnf. Use a dedicated maintenance user, or skip flush-logs if you’re journald-only.

Don’t put a root password in a logrotate file.

If you also need a database backup plan (separate from rotation), see production database backup strategies in 2026. Rotation reduces disk risk; it doesn’t protect your data.

Step 7 — systemd journal: cap it so it can’t eat your root filesystem

On systemd-based distros, journald grows until it hits its internal limit. That limit is often “a percentage of /var.”

On smaller VPS disks, that can still be far too generous.

Check current size:

sudo journalctl --disk-usage

Set explicit limits in /etc/systemd/journald.conf (create overrides if you prefer). This baseline works well for small-to-mid VPS hosts:

sudo editor /etc/systemd/journald.conf
[Journal]
SystemMaxUse=1G
SystemKeepFree=2G
SystemMaxFileSize=200M
MaxRetentionSec=14day

Reload journald:

sudo systemctl restart systemd-journald
sudo journalctl --disk-usage

Tune these numbers to your disk size and how much history you actually use.

If you’re unsure what’s realistic, Linux VPS capacity planning in 2026 helps you avoid long retentions on tiny root volumes.

Step 8 — Add a “disk full early warning” check (simple, effective)

Rotation is control; alerts are detection. Even without a monitoring stack, a daily cron or systemd timer can warn you when disk crosses a threshold.

Create /usr/local/sbin/disk-usage-check.sh:

sudo tee /usr/local/sbin/disk-usage-check.sh >/dev/null <<'EOF'
#!/bin/sh
set -eu
THRESHOLD="85"
MOUNT="/"
USE=$(df -P "$MOUNT" | awk 'NR==2{gsub(/%/,"",$5); print $5}')
if [ "$USE" -ge "$THRESHOLD" ]; then
  logger -p user.warning "Disk usage on $MOUNT is ${USE}% (threshold ${THRESHOLD}%)"
fi
EOF
sudo chmod 0755 /usr/local/sbin/disk-usage-check.sh

Run it now:

sudo /usr/local/sbin/disk-usage-check.sh
sudo tail -n 30 /var/log/syslog 2>/dev/null || sudo journalctl -n 30

Schedule it with cron:

sudo crontab -e
15 2 * * * /usr/local/sbin/disk-usage-check.sh

If you later build a full monitoring stack, keep it focused and low-noise: disk, load, memory, and HTTP error rates.

Linux VPS performance monitoring in 2026 has a practical checklist that maps well to day-to-day hosting ops.

Step 9 — Validate your whole logrotate configuration (and read the state file)

After you edit a few policies, test the full run. On any distro:

sudo logrotate -d /etc/logrotate.conf

Then force a run. This is useful during a maintenance window:

sudo logrotate -vf /etc/logrotate.conf

Logrotate records what it rotated in /var/lib/logrotate/status. If you’re convinced “rotation isn’t happening,” this file usually explains why.

Common causes include “already rotated today,” a path mismatch, or missing files.

sudo tail -n 80 /var/lib/logrotate/status

Step 10 — Common breakages and how to fix them fast

  • “Permission denied” during rotation
    Add su inside the specific block (preferred), and make sure directory permissions allow rotation. Check ownership: namei -l /var/log/nginx.
  • Logs stop updating after rotation
    The daemon didn’t reopen the file. Fix the postrotate reload, and verify the command exists (nginx -t, apachectl configtest).
  • Rotated files aren’t compressed
    If you used delaycompress, the newest rotated file stays uncompressed until the next run. That’s expected behavior.
  • Rotation creates logs with wrong owner/mode
    Correct the create line to match the daemon user/group. Then generate traffic and check with ls -l.
  • Disk still fills even with rotation
    Usually it’s (a) app logs outside /var/log, (b) large temp files in /tmp, or (c) deleted-but-open files. Find open deleted files: sudo lsof +L1.

Optional: ship logs off the VPS (so you can keep retention short locally)

If you need 30–90 days of logs for compliance or incident response, don’t keep all of that on the VPS. Rotate locally for disk safety, then forward logs elsewhere for retention.

If that’s your next step, follow Linux VPS log shipping with rsyslog and keep local retention lean (7–14 days).

Summary: a practical log rotation baseline for hosting servers

A good Linux VPS log rotation setup should feel unremarkable. Rotate web logs daily. Rotate app and database logs weekly. Cap journald.

Then confirm daemons reopen files after rotation.

Do that, and disk-full outages become an exception instead of a pattern.

If you want this handled alongside patching, web stack updates, and backup checks, managed VPS hosting from HostMyCode is the clean option. If you prefer full control, start with a fast NVMe plan on a HostMyCode VPS and apply the policies above on day one.

If you run client sites or production WordPress on your own server, log growth is an easy way to get surprised by downtime. HostMyCode can provision a VPS with predictable storage performance and help you stay on top of the basics—rotation, backups, and updates.

Start with a HostMyCode VPS, or hand off the ongoing work to managed VPS hosting if you’d rather focus on your sites than the server.

FAQ

Should I use copytruncate to rotate logs without reloading services?

Use it only when you can’t reload the service. Reloading is safer and keeps log integrity intact. copytruncate can drop lines during high write rates.

How many days of logs should I keep on a VPS?

For most hosting servers, 7–14 days locally is enough if you also ship logs or have backups. High-traffic sites often need shorter retention unless you allocate extra disk.

Why did my log file become owned by root after rotation?

Your logrotate block likely has a create line with the wrong owner/group. Set it to the daemon user (for example, www-data or nginx) and verify with ls -l after a forced rotation.

How do I confirm Nginx is writing to the new log file after rotation?

Force a rotation, reload Nginx, then watch the new file size increase. For a definitive check, use lsof to confirm which path the Nginx worker processes have open.

Does journald replace logrotate?

No. journald has its own retention controls, while many services still write plain files in /var/log. Treat them as separate storage pools and cap both.

Linux VPS log rotation setup: prevent disk full outages with logrotate + systemd (2026 tutorial) | HostMyCode