A VPS running web servers, databases, and applications continuously generates log files. Without proper log management, these files accumulate and eventually fill your entire disk, causing your website to crash and preventing the database from writing data. Logrotate is the standard Linux utility that automatically rotates, compresses, and deletes old logs on a schedule, ensuring your VPS never runs out of disk space due to logs. This comprehensive guide walks you through setting up logrotate for all common services on your VPS.
Why Log Rotation Matters on a VPS
Log files are essential for monitoring application behavior, diagnosing errors, and tracking security events. However, high-traffic services like Nginx, Apache, and MySQL generate hundreds of megabytes or even gigabytes of log data per week. Without rotation, a single log file can grow to tens of gigabytes in size, consuming SSD space that should be reserved for your website and database files.
When disk space fills to 100%, several catastrophic things can happen:
- Your web server stops serving pages and returns 507 Insufficient Storage errors
- The database cannot write records, causing application errors and data loss
- Cron jobs fail to execute, breaking scheduled backups and automated tasks
- The VPS becomes unstable and may experience unexpected reboots
- SSH access may be impaired or unavailable
Logrotate solves this by automatically moving old logs to archives, compressing them to save space, and deleting logs that exceed your retention policy. By implementing logrotate correctly, you ensure your VPS has consistent available disk space and your logs remain available for troubleshooting when needed.
Understanding Logrotate Architecture
Logrotate operates through several key components that work together seamlessly:
Main Configuration Files
/etc/logrotate.conf # Global default settings for all logs
/etc/logrotate.d/ # Directory for service-specific config files
/var/lib/logrotate/status # Database tracking which logs were rotated when
The global config file /etc/logrotate.conf sets sensible defaults that apply to all logs. Service-specific configs in /etc/logrotate.d/ override these defaults for particular services. For example, Nginx might rotate logs daily and keep 14 days, while MySQL might keep only 7 days and compress immediately.
The status file is logrotate's memory — it tracks which logs have been rotated and when the last rotation occurred. This prevents the same log from being rotated multiple times on the same day, even if logrotate runs multiple times via cron.
How Logrotate Works
When logrotate runs (typically once daily via cron), it:
- Reads all config files from /etc/logrotate.conf and /etc/logrotate.d/
- Compares each log file's size and age against rotation conditions (daily, weekly, size threshold)
- For logs that need rotation:
- Renames the active log file (e.g., access.log → access.log.1)
- Creates a new empty log file with the same name and permissions
- Optionally compresses old logs to .gz format
- Deletes old logs that exceed the retention count
- Runs optional post-rotation scripts (e.g., sending a signal to Nginx to reopen its log)
- Updates /var/lib/logrotate/status with the rotation timestamp
This happens silently in the background without requiring manual intervention.
Essential Logrotate Options Explained
Understanding the key configuration options helps you tailor logrotate to each service's needs:
Rotation Frequency
daily— rotate logs every 24 hours (most common)weekly— rotate once per 7 daysmonthly— rotate once per 30 dayssize 100M— rotate when file exceeds 100MB, regardless of date (good for high-traffic services)
Retention Policy
rotate N— keep N most recent archived logs (older ones are deleted). For example,rotate 14keeps 14 days of compressed logs.maxage N— delete logs older than N days, regardless of the rotate count
Compression
compress— gzip old logs immediately after rotationdelaycompress— delay compression until the next rotation (used with compress). This is needed when a service process still holds the file open.nocompress— don't compress logs (useful for text logs you access frequently)
File Handling
missingok— don't error if the log file doesn't existnotifempty— skip rotation if the log file is empty (saves space when services are idle)create 640 user group— create a new log file with specified permissions and ownership (e.g.,create 640 www-data admfor Nginx)copytruncate— copy the log file to an archive and truncate the original, rather than rename (for apps that don't handle file descriptor changes)
Service Integration
sharedscripts— run postrotate scripts once even if the rule matches multiple filespostrotate ... endscript— shell commands to run after rotation (e.g., sending SIGHUP to reload a service)prerotate ... endscript— shell commands to run before rotation
Configuring Logrotate for Nginx
Nginx is the most common web server on AsiaGB VPS plans. Nginx typically rotates its logs daily and keeps 14 days of history:
sudo nano /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 640 www-data adm
sharedscripts
postrotate
if [ -f /var/run/nginx.pid ]; then
kill -USR1 `cat /var/run/nginx.pid`
fi
endscript
}
Let's break down this configuration:
daily— rotates logs every 24 hoursrotate 14— keeps 14 days of compressed archives before deletioncompress + delaycompress— gzip old logs, but wait one rotation cycle before compressing the most recent archive. This allows Nginx to finish writing to the previous log.create 640 www-data adm— creates a new access.log and error.log owned by www-data (Nginx user) with permissions 640postrotate script— sends SIGUSR1 (signal 10) to Nginx, telling it to reopen log files and start writing to the new access.log without requiring a full restart
After editing, test the configuration with sudo logrotate -d /etc/logrotate.d/nginx to ensure logrotate interprets it correctly before relying on it.
Configuring Logrotate for MySQL and MariaDB
MySQL slow query logs and error logs grow at different rates depending on query complexity and traffic volume. A typical MySQL configuration:
sudo nano /etc/logrotate.d/mysql
/var/log/mysql/error.log
/var/log/mysql/mysql-slow.log {
daily
rotate 7
missingok
compress
delaycompress
notifempty
create 640 mysql adm
postrotate
# Tell MySQL to flush logs and use new files
mysqladmin -u root -p'YOUR_ROOT_PASSWORD' flush-logs 2>/dev/null || true
endscript
}
Key differences from Nginx:
rotate 7— MySQL logs are often larger, so keep only 7 days instead of 14postrotaterunsmysqladmin flush-logs, which tells MySQL to close the current log files and open new ones (equivalent to Nginx's reload)- The
|| trueat the end ensures the script doesn't fail if the MySQL command has a minor issue (prevents logrotate from reporting an error)
Replace YOUR_ROOT_PASSWORD with your actual MySQL root password, or use an option file like ~/.my.cnf if you have one configured for passwordless access.
Configuring Logrotate for Custom Application Logs
If you're running a custom application (Node.js, Python, Java, etc.), you'll need a custom logrotate config:
sudo nano /etc/logrotate.d/myapp
/var/www/myapp/logs/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
copytruncate
su www-data www-data
}
This configuration is appropriate for apps that don't handle log file descriptor changes gracefully:
rotate 30— keep 30 days of logs (apps generate logs at predictable rates, so longer retention is often acceptable)copytruncate— copy the current log to an archive file, then truncate the original to zero bytes. The app continues writing to the same file without reopening, avoiding potential file descriptor issues.su www-data www-data— run logrotate under the www-data user/group (app user), ensuring proper file permissions on rotation
The key here is copytruncate. If your app doesn't respond well to file descriptor switching (as with nginx's reload), copytruncate is safer because the app never stops writing to the file — it's just truncated.
Advanced: Size-Based Rotation
For high-traffic services, you may want to rotate logs when they exceed a certain size rather than waiting for a daily rotation:
sudo nano /etc/logrotate.d/hightraffic
/var/log/myapp/access.log {
size 100M
rotate 5
compress
missingok
copytruncate
}
This configuration rotates access.log whenever it exceeds 100 megabytes, regardless of the time of day. Useful for apps that log heavily and you want to prevent any single log file from growing too large. Keep in mind that size-based rotation ignores the daily/weekly/monthly schedule.
Testing and Debugging Logrotate
Before trusting logrotate with your production logs, always test your configuration:
Dry Run (Debug Mode)
# Test without making changes
sudo logrotate -d /etc/logrotate.conf
# Or test a specific service config
sudo logrotate -d /etc/logrotate.d/nginx
The -d flag shows exactly what logrotate would do without touching any files. Study the output carefully for any errors or unexpected behavior.
Verbose Run
# Execute with detailed output
sudo logrotate -v /etc/logrotate.conf
Force Rotation
# Force rotation immediately (useful for testing)
sudo logrotate -f /etc/logrotate.d/nginx
View Rotation History
# Check which logs were rotated when
sudo cat /var/lib/logrotate/status
Output shows each log file and the date it was last rotated. This helps confirm that logrotate is running on schedule.
Managing systemd Journal Logs
Modern Ubuntu and Debian systems use systemd's journal service, which also accumulates log data over time. If your VPS is running systemd (most do), monitor and manage journal logs separately from traditional /var/log logs:
Check Journal Disk Usage
journalctl --disk-usage
This shows how much space the journal is consuming. Large systems might find 500MB–2GB of journal logs.
Clean Up Journal Logs
# Vacuum logs older than 30 days
sudo journalctl --vacuum-time=30d
# Vacuum to keep only 500MB
sudo journalctl --vacuum-size=500M
# Vacuum to keep logs from the last 7 days
sudo journalctl --vacuum-time=7d
Permanent Journal Limits
Set permanent limits in the systemd journald configuration so the journal never grows beyond a threshold:
sudo nano /etc/systemd/journald.conf
Add or uncomment these lines:
SystemMaxUse=500M
MaxRetentionSec=30day
ForwardToSyslog=yes
Then restart the journal service:
sudo systemctl restart systemd-journald
SystemMaxUse=500M caps the total journal size at 500 megabytes. MaxRetentionSec=30day deletes entries older than 30 days. Adjust these values based on your VPS disk size and retention requirements.
Finding What's Filling Your Disk
Sometimes log rotation isn't catching a particular runaway log file. Use these commands to find the largest files and directories:
See Overall Disk Usage
df -h
Shows used/available space on all mounted filesystems. If any partition is above 85% full, investigate immediately.
Find Large Directories
# Show top 20 directories consuming the most space
du -sh /var/log/* | sort -rh | head -20
This quickly identifies which service is logging the most data.
Find Large Individual Files
# Find all files larger than 100MB in /var/log
find /var/log -type f -size +100M -exec ls -lh {} \;
Use this to spot any log files that haven't been rotated yet.
Best Practices and Troubleshooting
Always Dry-Run Before Deploying
Before relying on a new logrotate config in production, test it:
sudo logrotate -d /etc/logrotate.d/yourservice
Review the output for any errors. Common issues include wrong file paths, permission problems, and incorrect script syntax.
Monitor Rotation Actually Happening
After your first deployment, manually check that logrotate is working:
ls -la /var/log/nginx/
# Should show access.log.1.gz, access.log.2.gz, etc.
Avoid Compressing Critical Logs You Access Frequently
If you frequently grep or tail recent logs, consider delaying compression:
compress
delaycompress # Delay one rotation cycle
This keeps the most recent archive uncompressed and readable while still saving space on older archives.
Use postrotate Scripts Wisely
Postrotate scripts are powerful but can fail silently. Always include error handling:
postrotate
# Send signal with error checking
if [ -f /var/run/service.pid ]; then
kill -USR1 `cat /var/run/service.pid` 2>/dev/null || true
fi
endscript
Pro Tip: Store custom logrotate configs for all your services in /etc/logrotate.d/ rather than modifying the global /etc/logrotate.conf. This keeps your custom configuration isolated and makes it easier to back up and migrate your VPS.
Automated Disk Health Monitoring
Once logrotate is in place, consider adding a simple monitoring script to alert you if disk usage approaches full:
#!/bin/bash
THRESHOLD=85
USAGE=$(df / | awk 'NR==2 {print $5}' | sed 's/%//')
if [ $USAGE -gt $THRESHOLD ]; then
echo "WARNING: Disk usage at $USAGE%" | mail -s "VPS Disk Alert" [email protected]
fi
Add this to your crontab to run daily. It sends an email alert if disk usage exceeds 85%, giving you time to investigate before the disk fills completely.
Need a Linux VPS with Full Disk Control?
AsiaGB offers SSD-based VPS hosting on Ubuntu/Debian with full root access — manage logs, cron jobs, and services exactly as you need. Uptime 99%, starting at 500 THB/month with 24-hour support.
View VPS Plans