How to Set Up Log Rotation on Fedora (logrotate)

Set up log rotation on Fedora by creating a config file in /etc/logrotate.d/ and running logrotate -f to test it.

You deployed a service and the logs are eating your disk

You deployed a custom service on Fedora. It has been running for three weeks. You check disk usage and see /var/log/myapp.log is 4GB. The service is still writing to the same file. Tomorrow it hits 10GB. Next week your root partition fills up and the system becomes unresponsive. You need the logs to rotate, compress, and clean themselves up without manual intervention.

How logrotate manages log lifecycle

logrotate is the tool that manages log files. Think of it like a notebook with limited pages. When you fill a page, you do not erase it. You turn to a fresh page and keep writing. The old pages stay bound in the back for reference. Eventually, you discard the oldest pages to make room. logrotate does this for files. It renames the current log, creates a new empty one, compresses the old ones, and deletes files older than your retention policy.

Fedora ships with logrotate pre-installed. The main config is /etc/logrotate.conf. You never edit that file directly. You drop per-service configs into /etc/logrotate.d/. This keeps your changes safe across package updates. The package manager will not overwrite files in /etc/logrotate.d/ when logrotate gets updated.

Fedora runs logrotate via a systemd timer. You can check the status with systemctl status logrotate.timer. The timer triggers the rotation daily. You do not need to add it to cron.

Run logrotate -d before you save. A syntax error stops rotation until you fix it.

Set up rotation for a custom service

Create a configuration file in the drop-in directory. Use a name that matches your service for clarity.

sudo nano /etc/logrotate.d/myapp
# WHY: /etc/logrotate.d/ is the drop-in directory for custom rotation rules.
#      Files here are included by the main config automatically.
#      The filename does not affect behavior, but matching the service name helps with maintenance.

Add the configuration block. Replace /var/log/myapp.log with your actual log path.

/var/log/myapp.log {
    daily
    # WHY: Rotate the log once every day.
    #      Use 'weekly' or 'monthly' if your app produces less noise.

    rotate 7
    # WHY: Keep 7 rotated files before deleting the oldest.
    #      Adjust based on disk space and how far back you need to debug.

    compress
    # WHY: Gzip the rotated logs to save disk space.
    #      Compressed files are smaller but take a moment to read.

    delaycompress
    # WHY: Skip compression on the most recent rotated file.
    #      This prevents issues if the app keeps the old file open
    #      and tries to write to it before the next rotation.

    missingok
    # WHY: Don't throw an error if the log file doesn't exist.
    #      Useful for services that might not have started yet.

    notifempty
    # WHY: Skip rotation if the log file is empty.
    #      Saves cycles and avoids creating useless .1 files.

    create 0640 root root
    # WHY: Create a new log file with specific permissions after rotation.
    #      0640 means owner can read/write, group can read, others have no access.
    #      Match the user/group that your application runs as.

    postrotate
        /usr/bin/systemctl reload myapp > /dev/null 2>&1 || true
    # WHY: Run a command after rotation to tell the app to reopen the log file.
    #      Many apps hold the file descriptor open. Without this, they keep
    #      writing to the renamed file instead of the new empty one.
    #      The '|| true' ensures logrotate doesn't fail if the reload hangs.
    endscript
}

Save the file. Test the configuration with a dry run before forcing rotation.

sudo logrotate -d /etc/logrotate.d/myapp
# WHY: The -d flag runs a debug dry-run.
#      It shows exactly what logrotate would do without touching any files.
#      Always run this first to catch syntax errors or permission issues.

If the dry run produces no errors, force a rotation to verify the setup works immediately.

sudo logrotate -f /etc/logrotate.d/myapp
# WHY: The -f flag forces rotation even if the time interval hasn't passed.
#      Use this once to verify the setup works immediately.
#      Do not use -f in production scripts; it ignores your schedule.

Check the postrotate script. If the app does not reopen the file, rotation is useless.

Verify the configuration works

List the log directory to confirm the files were created and rotated.

ls -lh /var/log/myapp.log*
# WHY: Verify that myapp.log exists as a new small file.
#      Check for myapp.log.1 which should be the recent uncompressed rotation.
#      Older files should have .gz extensions if compress is enabled.

You should see myapp.log as a new, small file. You should see myapp.log.1 as the most recent rotated file. Because of delaycompress, myapp.log.1 is not compressed yet. The next rotation will compress it.

If your application writes to the journal instead of a flat file, check the journal size.

journalctl --disk-usage
# WHY: systemd manages journal rotation automatically.
#      This command shows the total size of all journal files.
#      You do not need logrotate for journal logs.

Trust the state file. Manual file manipulation breaks the rotation sequence.

Common pitfalls and error patterns

The debug run will print error: skipping /var/log/myapp.log because it doesn't seem to be a regular file if the path is wrong or points to a directory. Check your path. Ensure the file exists and is a regular file, not a symlink to a directory.

If you see error: could not open configuration file /etc/logrotate.d/myapp: Permission denied, check the file permissions. The file must be readable by root. Run sudo chmod 644 /etc/logrotate.d/myapp to fix standard permissions.

If the new log file is empty but myapp.log.1 is growing, your application did not reopen the file. The postrotate script is missing or failing. Check journalctl -u logrotate for errors. Many applications require a signal to reopen logs. If your app does not support systemctl reload, you may need to send a USR1 signal or restart the service.

Some applications cannot be signaled to reopen the log file. They just keep writing to the file descriptor. For these apps, you use copytruncate. This copies the log, truncates the original, and leaves the app writing to the same file. The downside is a small window where logs might be lost if the app writes during the copy. Use postrotate whenever possible. It is safer. Use copytruncate only when the app has no reload mechanism.

logrotate tracks what it has done in /var/lib/logrotate/status. If you manually delete rotated files, logrotate might get confused about the sequence. Let logrotate manage the lifecycle. Do not manually delete .1 files and expect logrotate to renumber them.

Log files often contain sensitive data. The create directive sets permissions on the new file. The rotated files keep the permissions of the original unless you use the su directive. If your app runs as a non-root user, you might need su root root in the config to allow logrotate to access the file. This is a common trap. Add su root root if you get permission errors on the log file itself.

Run journalctl -xe first. Read the actual error before guessing.

Choose the right rotation strategy

Use logrotate when you have a custom application writing to a flat file in /var/log/.

Use copytruncate when the application cannot reopen the log file and has no reload signal.

Use postrotate when the application supports reloading configuration or reopening file descriptors.

Use journalctl when your service integrates with systemd and writes to the journal.

Use rsyslog when you need to forward logs to a remote collector or apply complex filtering rules.

Use Cockpit's log viewer when you want a quick web-based interface to browse logs without touching the terminal.

Stay with default package configs when the software already ships a working rule in /etc/logrotate.d/.

Where to go next