Every self-hosted server generates log files. Left unchecked, these logs consume disk space, degrade performance, and make troubleshooting harder. Log rotation is the fundamental sysadmin practice of archiving, compressing, and deleting old log files on a schedule. But which tool should you use?
In this guide, we compare three proven open-source log rotation approaches: logrotate (the universal standard), s6-log (modern supervision-suite logger), and svlogd (runit’s reliable logger). Each has a fundamentally different design philosophy, and understanding these differences helps you choose the right tool for your infrastructure.
Why Log Rotation Matters for Self-Hosted Servers
Without log rotation, a busy server can fill its disk in days. A single nginx access log on a medium-traffic site grows 500MB–2GB per day. Application logs from services like rsyslog or Vector compound the problem. The consequences of unmanaged logs include:
- Disk exhaustion —
/varfills up, causing services to crash - Slow log analysis — Searching 50GB monolithic log files is impractical
- Backup bloat — Uncompressed logs waste backup storage and bandwidth
- Security compliance — Many regulations (SOC 2, HIPAA) require log retention policies
- Performance degradation — Writing to ever-growing files causes I/O bottlenecks
Proper log rotation solves all of these by automatically compressing old logs, enforcing retention periods, and signaling services to reopen log files after rotation.
For servers that also need tamper-evident logging for compliance, see our log integrity and audit logging guide.
Logrotate: The Universal Standard
logrotate is the default log rotation tool on virtually every Linux distribution. First released in 1996, it remains the most widely deployed log rotation utility. It uses a cron-based scheduling model with a simple, declarative configuration syntax.
GitHub: github.com/logrotate/logrotate | ⭐ 1,519 | Updated: April 2026 | Language: C
Installation
| |
Configuration
Logrotate reads configuration from /etc/logrotate.conf and /etc/logrotate.d/. Each service gets its own file in logrotate.d/. Here’s a production-ready nginx configuration:
| |
Key directives explained:
| Directive | Purpose | Example Value |
|---|---|---|
daily / weekly / monthly | Rotation frequency | daily |
rotate N | Number of rotated files to keep | rotate 14 (2 weeks) |
compress | gzip old rotated files | compress |
delaycompress | Don’t compress the most recent rotated file | delaycompress |
missingok | Don’t error if log file is missing | missingok |
notifempty | Skip rotation if log is empty | notifempty |
size | Rotate when file exceeds size | size 100M |
maxage | Remove rotated files older than N days | maxage 30 |
minsize | Minimum file size before rotation triggers | minsize 1M |
copytruncate | Copy then truncate (for apps that can’t reopen logs) | copytruncate |
dateext | Use date-based naming instead of numeric | dateext |
dateformat | Format for date-based file names | -%Y%m%d |
Testing and Debugging
| |
Cron Setup
Logrotate is typically triggered by a daily cron job at /etc/cron.daily/logrotate:
| |
For more frequent rotation (hourly), add to /etc/cron.hourly/:
| |
Docker Integration
When running services in containers, logrotate can manage logs on the host volume:
| |
s6-log: Modern Supervision-Suite Logger
s6-log is the logging component of the s6 supervision suite, a modern process supervision system designed as an alternative to systemd and supervisord. Unlike logrotate’s cron-based approach, s6-log runs as a persistent daemon that automatically handles log rotation, timestamping, and archival in real-time.
GitHub: github.com/skarnet/s6 | ⭐ 920 | Updated: April 2026 | Language: C
Installation
| |
How s6-log Works
s6-log operates differently from logrotate. Instead of rotating existing files on a schedule, it reads log data from a pipe (typically via s6-log or the s6-fdholderd pipeline) and manages the output files internally. The daemon handles:
- Automatic size-based rotation
- Timestamp prefixing (ISO 8601 or TAI64N)
- Automatic compression of archived logs
- Directory-based log storage
- Real-time processing (no cron dependency)
Configuration
Create a log directory structure for your service:
| |
For advanced configuration with size limits, rotation count, and timestamping:
| |
s6-log options explained:
| Option | Purpose | Example |
|---|---|---|
-t | Add TAI64N timestamps to each line | -t |
-n N | Keep at most N files | n10 (keep 10 files) |
-s N | Rotate when file reaches N bytes | s1000000 (1MB) |
-p mode | Set file permissions | -p 0640 |
-u uid:gid | Set file ownership | -u daemon:daemon |
!pattern | Exclude lines matching pattern | "!w*" (skip warnings) |
+pattern | Include only lines matching pattern | "+error" |
!{pattern} | Exclude and archive matching lines | "!{debug}" |
Complete Service Example
Here’s a full s6 service definition with logging for a web server:
| |
This setup automatically:
- Captures nginx stdout/stderr via the service supervisor
- Adds TAI64N timestamps to every log line
- Rotates when files reach 5MB
- Keeps the last 30 rotated files
- Runs continuously without cron
svlogd: Runit’s Simple Logger
svlogd is the logging daemon from the runit service supervision framework. It shares a similar philosophy with s6-log — real-time log processing rather than scheduled rotation — but with a simpler, more minimal design.
Installation
| |
How svlogd Works
Like s6-log, svlogd runs as a persistent daemon that reads log data from stdin (typically piped from a service’s stdout). It stores logs in a directory structure and handles rotation based on size thresholds.
Configuration
svlogd is controlled by a config file in the log directory:
| |
Config file format (one directive per line, no spaces):
| Directive | Purpose | Example |
|---|---|---|
sNNN | Maximum file size in bytes | s1000000 (1MB per file) |
nNN | Maximum number of files | n20 (keep 20 files) |
t | Add ISO 8601 timestamps | t |
!cmd | Pipe rotated files through command | !gzip |
uNNN | Umask for new files | u027 |
pNNNN | File permissions | p0640 |
Log Directory Run Script
| |
The -tt flag adds timestamps and enables verbose output. The service log script simply pipes stdout to svlogd:
| |
Complete Service Example with Docker
Here’s a practical example running a service under runit with svlogd:
| |
Feature Comparison
| Feature | logrotate | s6-log | svlogd |
|---|---|---|---|
| Architecture | Cron-based daemon | Persistent supervisor daemon | Persistent supervisor daemon |
| Scheduling | Time/size via cron | Real-time, size-based | Real-time, size-based |
| Configuration | Declarative config files | execlineb scripts | Single config file |
| Compression | gzip/bzip2/xz | Built-in gzip | Via pipe (!gzip) |
| Timestamps | dateext for filenames | TAI64N per-line | ISO 8601 per-line |
| Service Integration | postrotate signals | Native s6 supervision | Native runit supervision |
| Pattern Filtering | No | Yes (include/exclude) | No |
| Learning Curve | Low | Medium | Low |
| Docker Friendly | Yes (host volume) | Yes (in-container) | Yes (in-container) |
| Dependencies | cron | s6 supervision suite | runit |
| Package Availability | All major distros | Debian, Alpine, source | All major distros |
| GitHub Stars | 1,519 | 920 (s6 suite) | N/A (mirror repos) |
Choosing the Right Tool
Use logrotate when:
- You’re on a standard Linux server and want the path of least resistance
- You need time-based rotation (daily, weekly, monthly)
- You want declarative configuration that any sysadmin can read
- You’re managing logs for services that can’t be easily supervised
- You need post-rotation hooks (compress, mail, execute scripts)
Use s6-log when:
- You’re already using the s6 supervision suite
- You want real-time log processing without cron
- You need per-line timestamping and pattern-based filtering
- You’re building container images and want minimal dependencies
- You prefer the execlineb scripting approach
Use svlogd when:
- You’re using runit as your init/supervision system
- You want the simplest possible logger with minimal configuration
- You need reliable, battle-tested log rotation (runit has been stable since 2001)
- You want ISO 8601 timestamps out of the box
- You prefer shell-script-based service definitions
Performance and Resource Usage
Resource consumption is critical for self-hosted servers running on limited hardware. Here’s how the three tools compare:
| |
Typical resource usage on a production server:
| Metric | logrotate | s6-log | svlogd |
|---|---|---|---|
| Memory (RSS) | 0 (runs then exits) | ~1–2 MB (persistent) | ~1–2 MB (persistent) |
| CPU during rotation | Brief spike (ms) | None (continuous) | None (continuous) |
| Disk I/O pattern | Burst during rotation | Steady, small writes | Steady, small writes |
| Latency impact | Milliseconds per cron run | Near-zero (background) | Near-zero (background) |
Security Considerations
Proper log rotation is part of a comprehensive server security posture:
| |
For services that handle sensitive data, combine log rotation with log shipping to a centralized logging system. Our syslog aggregation guide covers forwarding rotated logs to a central server.
FAQ
What is the difference between logrotate and s6-log?
logrotate runs on a schedule (typically via cron) and rotates existing log files by renaming, compressing, and truncating them. s6-log runs as a persistent daemon that processes log data in real-time through a pipe, automatically managing rotation based on file size. logrotate is better for traditional server setups, while s6-log excels in service-supervision environments.
Can I use logrotate with Docker containers?
Yes. logrotate runs on the host and can manage container log files stored on host volumes. Docker’s default logging driver stores container logs at /var/lib/docker/containers/<id>/<id>-json.log. You can configure logrotate to manage these files using copytruncate since Docker doesn’t support log reopen signals:
| |
Alternatively, use Docker’s built-in --log-opt max-size and --log-opt max-file flags in your docker-compose configuration.
Which log rotation tool is best for a small VPS?
For a small VPS (1–2 GB RAM), logrotate is the best choice. It has zero persistent memory overhead since it runs briefly via cron and exits. Both s6-log and svlogd add a persistent daemon consuming 1–2 MB of RAM, which is negligible but unnecessary if you don’t already use their respective supervision suites.
How do I rotate logs for an application that doesn’t support log file reopening?
Use the copytruncate directive in logrotate. It copies the current log file to a new file (for rotation) and then truncates the original to zero bytes. The application continues writing to the same file descriptor without needing to be signaled:
| |
Note that there’s a tiny window between the copy and truncate where log lines may be lost. For critical applications, use a proper signal-based rotation with postrotate.
Does s6-log support log compression?
Yes, s6-log supports automatic gzip compression. You can configure it by adding the z option or using a processor script. The s6 package also includes s6-fdholderd for managing file descriptors across service restarts, which ensures logs are never lost during service transitions.
How do I test my logrotate configuration before deploying?
Run logrotate with the -d (debug/dry-run) flag to see exactly what it would do without making any changes:
| |
This prints a detailed report of which files would be rotated, compressed, or deleted. For a forced rotation test, use -f instead of -d, but be aware this will actually rotate the files.
Can I use s6-log or svlogd without their full supervision suite?
Technically yes, but it’s not straightforward. Both tools are designed to be fed log data through pipes from their respective supervision systems. You can manually pipe application output to them (myapp 2>&1 | s6-log /var/log/myapp), but you lose the automatic process management benefits. If you only need log rotation without supervision, logrotate is a better fit.