How to Install and Enable Fstrim Timer on Fedora

Enable the fstrim timer on Fedora by editing the systemd unit to set a daily interval and starting the service.

You installed Fedora on an NVMe drive and forgot about maintenance

You just finished installing Fedora on a fast NVMe SSD. The system boots in seconds and applications launch instantly. Three months later, you notice file copies taking longer than they used to. You read a forum thread about SSD garbage collection and realize your drive has never sent a TRIM command to the controller. You need to automate the process without turning your desktop into a manual maintenance chore.

What's actually happening under the hood

Solid-state drives do not overwrite data in place. When you delete a file, the operating system marks the space as free in the filesystem table. The physical flash cells remain filled until the drive's internal garbage collector runs. Without a TRIM signal, the controller assumes every block might still be in use. It spends extra cycles moving valid data around to make room for new writes. This slows down sequential and random writes over time.

Linux historically left TRIM to the user. Early SSD firmware had bugs that handled frequent TRIM commands poorly. Modern drives handle periodic TRIM without issue. Fedora ships with the fstrim utility and a systemd timer unit, but the timer is disabled by default. The default configuration runs once a week. Many users prefer a daily schedule or want to ensure it runs shortly after boot.

The fstrim command communicates with the kernel through the FITRIM ioctl. It scans mounted filesystems, identifies blocks marked as free, and passes those ranges to the block device driver. The driver translates the ranges into NVMe or SATA TRIM commands. The drive marks those physical cells as empty and clears them during its next background cycle. This keeps write amplification low and preserves drive lifespan.

Systemd timers replace cron for system maintenance tasks. They integrate directly with the journal, handle dependencies, and survive reboots without extra configuration. Overriding the default timer uses systemd's drop-in directory system. You never edit files in /usr/lib/systemd/. You create overrides in /etc/systemd/system/. This keeps your changes intact across package updates. Config files in /etc/ are user-modified. Files in /usr/lib/ ship with the package. Edit /etc/. Never edit /usr/lib/.

Run journalctl first. Read the actual error before guessing.

The fix or how-to

Check whether the timer is already active before making changes. Run the status command to see the current schedule and last trigger time. Always check status before restarting or modifying units.

systemctl status fstrim.timer
# WHY: Shows if the timer is loaded, active, and when it last ran
# WHY: Confirms whether you need to override the default weekly schedule
# WHY: Displays the next scheduled activation based on current config

If the output shows inactive (dead) or static, you need to enable it. Fedora uses a drop-in override to modify timer intervals without touching the original package files. The systemctl edit command opens your default editor and creates the correct directory structure automatically.

sudo systemctl edit fstrim.timer
# WHY: Opens a drop-in override file in /etc/systemd/system/fstrim.timer.d/
# WHY: Creates the directory and .conf file if they do not exist yet
# WHY: Prevents accidental edits to the upstream package unit file

Paste the timer configuration into the empty file. The IntervalSec value sets the recurring schedule. The OnBootSec value ensures the first run happens shortly after startup instead of waiting for the full interval to expire.

[Timer]
# WHY: Replaces the default weekly schedule with a daily run
IntervalSec=1d
# WHY: Triggers the first trim 15 minutes after boot
# WHY: Prevents waiting 24 hours for the initial garbage collection
OnBootSec=15min

Save the file and exit the editor. Systemd does not automatically detect drop-in changes. You must reload the daemon configuration so the new values take effect.

sudo systemctl daemon-reload
# WHY: Forces systemd to re-read all unit files and drop-in overrides
# WHY: Applies the new IntervalSec and OnBootSec values immediately
# WHY: Required after any manual change to /etc/systemd/system/

Enable the timer and start it for the current session. The --now flag combines enable and start in a single step.

sudo systemctl enable --now fstrim.timer
# WHY: Creates the symlink in the multi-user.target.wants directory
# WHY: Starts the timer immediately so it can schedule the first run
# WHY: Ensures the timer survives the next reboot automatically

Verify the timer is active and check the next scheduled run.

systemctl list-timers --all | grep fstrim
# WHY: Shows the next trigger time and the last activation
# WHY: Confirms systemd has parsed the new interval correctly
# WHY: Helps you spot misconfigured time units before they cause delays

Reboot before you debug. Half the time the symptom is gone.

Verify it worked

Timers run in the background. You need to check the journal to confirm the underlying service actually executed. The fstrim.service unit runs when the timer fires. Query the journal for the most recent activation. Use journalctl -xe for better context. The x flag adds explanatory text and the e flag jumps to the end. Most sysadmins type journalctl -xeu <unit> muscle-memory style.

journalctl -u fstrim.service --since "1 hour ago"
# WHY: Filters logs to the fstrim service unit
# WHY: Shows the exact filesystems trimmed and bytes freed
# WHY: Reveals any permission or ioctl errors during execution

Look for lines containing Trimmed followed by a size value. A typical first run looks like this:

# ...output truncated for clarity
fstrim: /: 12.4 GiB (13332530176 bytes) trimmed
fstrim: /home: 8.2 GiB (8804737536 bytes) trimmed

If the output shows 0 B trimmed, your filesystem might not support TRIM, or the mount options are missing the discard capability. Most modern ext4 and btrfs installations report several gigabytes on the first run.

You can also force a manual run to verify the command works outside the timer. This is useful for testing before relying on the schedule.

sudo fstrim -v /
# WHY: Runs fstrim on the root filesystem with verbose output
# WHY: Prints the exact number of bytes trimmed per mount point
# WHY: Bypasses the timer to test the FITRIM ioctl immediately

Snapshot the system before the upgrade. Future-you will thank you.

Common pitfalls and what the error looks like

The most common mistake is editing the original unit file in /usr/lib/systemd/system/fstrim.timer. Package managers overwrite files in that directory during updates. Your custom interval will disappear the next time systemd or systemd-libs updates. Always use systemctl edit or place files in /etc/systemd/system/.

Another frequent issue is confusing the timer with the discard mount option. Some users add discard to /etc/fstab to enable continuous TRIM. Continuous TRIM sends a command for every deleted file. This creates unnecessary I/O overhead and can reduce drive lifespan on older hardware. Periodic TRIM via fstrim.timer batches the commands and is the recommended approach for desktop and server workloads.

If the timer shows active but never runs, check for conflicting units or masked services. Run the following to see if anything is blocking it.

systemctl list-dependencies fstrim.timer
# WHY: Displays the dependency tree for the timer unit
# WHY: Reveals if a required target or service is masked or failed
# WHY: Helps you trace why the timer chain is not triggering

SELinux rarely blocks fstrim, but if you see denials in the journal, check the audit log. Fedora's default policy allows fstrim to run as part of the systemd maintenance context. SELinux denials show up in journalctl -t setroubleshoot with a one-line summary. Read those before disabling SELinux.

If you see Failed to trim /: Operation not supported in the journal, your filesystem does not support the FITRIM ioctl. This happens on older ext3 installations or certain network filesystems. Switch to ext4 or btrfs, or exclude the mount from the timer.

Trust the package manager. Manual file edits drift, snapshots stay.

When to use this vs alternatives

Use fstrim.timer when you want a reliable, journal-integrated schedule that survives reboots and package updates. Use the discard mount option when you are running a lightweight embedded system with minimal file deletions and want to avoid any stale blocks. Use manual fstrim commands when you need to trim specific mount points on demand, such as after a large database cleanup. Stay on the default weekly schedule if your drive handles garbage collection efficiently and you prefer lower background I/O.

Where to go next