Observability·8 min read·

logrotate: Keep VPS Logs From Filling the Disk

Configure logrotate so leftover text logs cannot fill a VPS root disk, with size caps, copytruncate versus create, and a dry-run habit before you need it.

NB

Netbay Infrastructure Team

Netbay Engineering

On this page

journald can be capped. Everything that still writes a file cannot. nginx access logs, application print statements, leftover rsyslog, backup scripts that echo to /var/log, and docker json-file logs will fill a VPS root disk on a quiet Tuesday if nobody rotates them. DiskAlmostFull is the alert. logrotate is the fix you install before the alert.

On a small plan the root filesystem is the only filesystem. High-Speed SSD does not help when used percent is 100. Observability that cannot write because /var is full is a dark box. Treat rotation as part of the metrics stack, not as an OS default you never read.

Inventory files that grow

Before you edit a config, find the growers. du -sh /var/log/* | sort -h. ls -lS /var/log. If docker is installed, du -sh /var/lib/docker/containers. If you still have a debug log in the app directory, it counts. Anything that grows without a date in the name is a candidate. Anything already managed by journald does not need a second rotator.

bash
sudo du -sh /var/log/* | sort -h
sudo journalctl --disk-usage
sudo logrotate --debug /etc/logrotate.conf
df -h / /var /var/log

logrotate --debug is a dry run. It prints what it would do without touching files. Run it after every edit. The weekly cron in /etc/cron.daily/logrotate is the live path on Debian and Ubuntu. systemd timer logrotate.timer is the same idea on newer images. Know which one is enabled.

Size-based rotation beats calendar on a VPS

weekly is a habit from machines that wrote slowly. A busy nginx access log can add gigabytes in a day. Use size 50M or maxsize 50M plus rotate 7 so a flood is capped in hours, not next Sunday. compress, delaycompress, missingok, notifempty are the usual flags. dateext makes the rotated name obvious in an incident.

nginx
/var/log/nginx/*.log {
    size 50M
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    sharedscripts
    postrotate
        /bin/systemctl reload nginx.service > /dev/null 2>/dev/null || true
    endscript
}

create versus copytruncate is the sharp edge. create (the default for most distro snippets) renames the file and creates a new one; the postrotate signal makes nginx open the new inode. copytruncate copies then truncates in place for daemons that never reopen. copytruncate can lose a few lines at rotate time. That is better than a daemon that keeps writing to a deleted inode until it fills the disk invisibly (lsof | awk for deleted). If you did not send the signal, df looks fine while the process holds the old file. Check lsof after the first rotate.

Do not rotate the journal with logrotate

/var/log/journal is not a text file. Leave it to journald vacuum. Double managing it is how you corrupt the journal. /var/log/syslog may still exist if rsyslog is installed; if ForwardToSyslog=no and you do not need rsyslog, disable the extra daemon rather than rotating its output forever.

Docker json-file logs are not logrotate's job unless you dropped a file in /etc/logrotate.d. Prefer daemon.json with max-size and max-file, or the local driver. Two rotation systems on the same container log fight. Pick one.

Application code that writes to a hardcoded path must either reopen on SIGUSR1 or use copytruncate. Better: log to stdout and let the unit journal it. Then logrotate has one fewer file and journald already has a cap.

Prove the cap with a flood test

On a non-production hour, write a dummy file that matches a rotate stanza and run logrotate --force. Confirm the compressed file appears, the live file is small, and the daemon still writes. Then fill a test file past size and wait for the timer, or run the cron script by hand. If DiskAlmostFull ever fires because of logs, the first commands are df -h, du -sh /var/log, journalctl --vacuum-size=200M, and logrotate --force. Put those four in the alert annotation.

Keep rotate counts small. Seven compressed 50 MB files is 350 MB plus the live file. That is a budget you can name. rotate 52 weekly uncompressed is how a forgotten access log eats the disk in a quarter. Observability is the budget, not the archive. If you need a year of nginx logs, ship them off the box; do not hoard them next to the OS.

Cap every writer on a single disk journald SystemMaxUse vacuum logrotate size 50M rotate 7 container logs daemon max-size one root filesystem, named budget reload after create, copytruncate if the daemon never reopens dry-run, force, then lsof for deleted inodes

Takeaway

Find every file that grows, rotate by size, signal daemons to reopen, and leave the journal to journald. A VPS that cannot write logs cannot tell you why it died. Set this up on Netbay in Lucknow before the disk hits 100 percent — netbayhosts.in.

Keep reading

Follow along on a real VPS

Deploy Linux in under 60 seconds

These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.

Deploy an instance