Skip to content
Products
AI Website Builder New VPS Hosting Cloud Servers Web Hosting Dedicated Servers Domains
Company
About Documentation Support Center Contact Get Started Login Client Area
ALL SYSTEMS OPERATIONAL
VPS

Set Up logrotate on a VPS Before Logs Fill Your Disk

Getwebup 5 min read

You free up disk space, restart the site, and three weeks later you're back at 100% full again. Nine times out of ten the culprit isn't backups or uploads — it's a log file that's been growing unchecked since the server was provisioned, because nobody ever told logrotate about it. Here's how to actually set it up so this stops happening.

Symptom: the disk fills up again and again

This one has a pattern. You run df -h, find / at 100%, clear some space, and everything's fine — until it isn't, a few weeks later. Common signs you're dealing with an unrotated log rather than a one-off:

  • du -sh /var/log/* shows one file that's several GB while everything else is tiny
  • A custom app log (Laravel's storage/logs/laravel.log, a Node app's app.log, PHP's error_log) is the single biggest thing on the disk
  • MySQL's slow query log or general log has been on since a debugging session months ago and never got turned off
  • journalctl --disk-usage reports several GB for systemd's journal

Find the actual offender before doing anything else:

du -ah /var/log 2>/dev/null | sort -rh | head -20
du -ah /home/*/logs /home/*/public_html 2>/dev/null | sort -rh | head -20

Cause: logrotate doesn't know about every log on your box

Most distros ship logrotate already installed and running daily via cron or a systemd timer, and it handles the standard system logs in /var/log out of the box — syslog, auth.log, apt history, that kind of thing. What it does not automatically handle:

  • Application logs your own code writes to a custom path (Laravel, a Node/PM2 process, a Python app under Gunicorn)
  • MySQL's slow query log or general query log if you turned them on for debugging
  • Nginx or Apache vhost-specific logs you added manually outside the default config
  • Docker container logs, which logrotate doesn't touch at all unless you configure the Docker daemon separately

Anything not covered by a logrotate config file just grows forever. There's no default cap.

Check what's already being rotated

# See every active logrotate config
cat /etc/logrotate.conf
ls /etc/logrotate.d/

# Dry-run to see what WOULD happen without actually rotating anything
logrotate -d /etc/logrotate.conf

If your app's log path doesn't show up anywhere in /etc/logrotate.d/, that's your answer.

Fix: write a config for the logs that are missing

Every logrotate config follows the same shape: which file(s), how often, how many to keep, and what to do after rotating. Create a new file per service in /etc/logrotate.d/ — don't edit the main logrotate.conf directly.

Example: a custom application log

sudo nano /etc/logrotate.d/myapp
/home/deploy/myapp/storage/logs/laravel.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    create 0640 deploy deploy
    sharedscript
    postrotate
        systemctl reload php8.2-fpm.service > /dev/null 2>&1 || true
    endscript
}

What each directive is actually doing:

DirectiveWhat it does
dailyRotate once a day (also: weekly, monthly, or size 50M to trigger by size instead of time)
rotate 14Keep 14 old copies before deleting the oldest
compressGzip rotated files to save space
delaycompressDon't compress the most recent rotation — keeps it readable if a process still has it open
missingokDon't error out if the log file doesn't exist yet
notifemptySkip rotation if the file is empty — avoids piling up empty .gz files
create 0640 deploy deployRecreate the log file with these permissions/owner right after rotating
postrotate / endscriptCommand to run after rotation — usually reloading the service so it reopens the new file handle

Example: MySQL slow query log

If you turned on slow query logging to chase down a performance issue and forgot to turn it off, add:

/var/log/mysql/mysql-slow.log {
    weekly
    rotate 4
    compress
    missingok
    notifempty
    create 0640 mysql mysql
    postrotate
        mysqladmin flush-logs 2>/dev/null || true
    endscript
}

MySQL needs the flush-logs call after rotation, otherwise it keeps writing to the now-renamed file instead of the fresh one — the log file gets deleted by rotation but the disk space isn't freed until the process stops holding it open.

Test before you trust it

Never wait for the daily cron to find out if your config is broken. Force it:

# Dry run - shows what would happen, changes nothing
logrotate -d /etc/logrotate.d/myapp

# Force an actual rotation right now, ignoring the schedule
sudo logrotate -f /etc/logrotate.d/myapp

# Confirm the new file exists with the right permissions
ls -la /home/deploy/myapp/storage/logs/

If the dry run errors on syntax, logrotate tells you the exact line. Fix it there before forcing a real run.

Systemd journal has its own limit

The journal isn't managed by logrotate at all — it's capped separately in /etc/systemd/journald.conf:

sudo nano /etc/systemd/journald.conf
# Set:
# SystemMaxUse=500M

sudo systemctl restart systemd-journald

# Or just trim it immediately without changing the permanent config:
sudo journalctl --vacuum-size=200M

Prevention: don't wait for the disk to fill up again

  • Whenever you deploy something new that writes its own log file — a queue worker, a cron script, a new app — add a logrotate config for it in the same PR/deploy, not as an afterthought later
  • Turn off MySQL's general query log and slow query log once you're done debugging with them — they're not meant to run permanently
  • Set up basic disk alerting (Netdata, a simple cron + df threshold script, or your monitoring tool of choice) so you get warned at 80% instead of finding out at 100%
  • If you're running Docker, configure log rotation on the daemon itself — logrotate can't see inside containers. Add {"log-driver": "json-file", "log-opts": {"max-size": "10m", "max-file": "3"}} to /etc/docker/daemon.json and restart Docker
  • Re-run logrotate -d /etc/logrotate.conf every few months as a sanity check — new services have a habit of adding logs nobody configured

A VPS that's been running for a year without anyone touching logrotate configs is almost always sitting on a few gigabytes of log bloat waiting to be found. Ten minutes writing a config file now saves you an emergency cleanup later.

Frequently asked questions

How often does the default logrotate cron actually run?

On most distros it's triggered daily by /etc/cron.daily/logrotate or a systemd timer (check `systemctl list-timers | grep logrotate`). It only rotates files listed in /etc/logrotate.conf and /etc/logrotate.d/ — anything not listed there is never touched, no matter how often the cron fires.

Why did the log file's disk space not actually free up after rotation?

If a running process still has the old file open (common with MySQL, Nginx, or a long-running Node/PHP worker), the disk space isn't released until that process closes the file handle. That's what postrotate hooks like `systemctl reload` or `mysqladmin flush-logs` are for — they tell the service to reopen its log file after rotation.

Should I use size-based or time-based rotation?

Time-based (daily/weekly) is predictable and easier to reason about for most app logs. Use size-based (`size 50M`) for logs with unpredictable volume, like a debug log that might be silent for weeks then flood during an incident — you don't want it growing unbounded between scheduled rotations.

Can I rotate multiple log files with one config block?

Yes — list multiple paths separated by spaces or newlines inside the same block, e.g. `/var/log/app/*.log { ... }`. Add `sharedscript` if you want the postrotate command to run once total instead of once per matched file.

Does logrotate handle the systemd journal too?

No, the journal is managed separately through /etc/systemd/journald.conf (SystemMaxUse, SystemKeepFree) or ad-hoc with `journalctl --vacuum-size=`. If journalctl --disk-usage shows several GB, that's a journald setting, not a logrotate config.

#logrotate #log-rotation #vps #disk-space #linux #sysadmin

Keep reading

Chat with Support