Blog » Linux » How to Use Systemd Timers in Linux: The Modern Cron Alternative
› how-to-use-systemd-timers-in-linux Linux terminal showing systemd timer commands with clock overlay representing scheduled task automation

How to Use Systemd Timers in Linux: The Modern Cron Alternative

Table of Contents

What Is a Systemd Timer?

If you’ve been scheduling tasks in Linux with cron and wondering whether there’s a better way, learning how to use systemd timers in Linux might be the upgrade you didn’t know you needed. A systemd timer is a unit file (ending in .timer) that triggers a matching service unit on a schedule. Think of it as a two-file system: backup.timer controls when something runs, and backup.service controls what runs.

Linux terminal showing systemd timer commands with clock overlay representing scheduled task automation

The best part? Systemd timers are built right into systemd. No extra daemon. No separate config syntax to memorize. If your distro uses systemd (and most do these days), you already have everything you need. If you’re new to service units, start with my guide on how to create a systemd service first.

I’ll be honest: I spent years writing crontab entries before I even touched systemd timers. It was around 2019 when I was setting up automated backups on my Proxmox homelab and kept losing cron output into the void. A friend on IRC mentioned timers and I haven’t looked back since. Once you see your scheduled task output neatly captured in journalctl, cron feels like writing to /dev/null on purpose.

Systemd Timer vs Cron: Why Bother Switching?

Cron works. It’s been working since the 1970s. So why learn something new? Because systemd timers solve real pain points that cron jobs in Linux simply can’t address without ugly workarounds.

RackNerd Mobile Leaderboard Banner

Get a VPS from as low as $11/year! WOW!

Here’s what you get with systemd timers:

  • Automatic logging: Every run is captured by journald with timestamps, exit codes, stdout, and stderr. No more redirecting output in Linux to log files manually.
  • Missed-run recovery: Set Persistent=true and if your system was off at the scheduled time, the task runs as soon as it boots back up.
  • No overlapping runs: If your task is still running when the next trigger fires, systemd waits. Cron doesn’t do this without manual file locking.
  • Dependency management: Timers can require other units to be active before firing.
  • Resource control: Use cgroup limits to cap CPU, memory, or I/O for your scheduled tasks.
Feature Cron Systemd Timer
Logging Manual (email/redirect) Automatic via journald
Missed runs Lost forever Persistent=true catches up
Overlap prevention Requires flock or PID files Built-in
Dependencies None Full unit dependency system
Resource limits None cgroup controls

That said, cron still has its place. If you need a quick one-liner on a system where portability matters, or you’re writing scripts that need to work across non-systemd distros, scheduling cron jobs is still perfectly valid.

Timer Types: Realtime vs Monotonic

Systemd timers come in two flavors. Understanding the difference is key to picking the right approach for your task.

Realtime Timers (OnCalendar): Run on Wall-Clock Events

Realtime timers use the OnCalendar= directive. They fire based on actual calendar dates and times, just like cron. Want something to run every day at 2:00 AM? That’s a realtime timer.

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true

Monotonic Timers (OnBootSec, OnUnitActiveSec): Relative Intervals

Monotonic timers fire relative to an event. OnBootSec=5min means “run 5 minutes after boot.” OnUnitActiveSec=1h means “run 1 hour after the last time this timer’s service activated.” These timers pause during system suspend.

A powerful pattern combines both for “run at boot, then repeat”:

[Timer]
OnBootSec=5min
OnUnitActiveSec=1h

This fires 5 minutes after boot, then every hour after that. I use this exact pattern on my homelab for health-check scripts that need to start quickly but keep running on a loop.

Creating Your First Systemd Timer (Step-by-Step)

Let’s build a real timer from scratch. We’ll create a daily backup script timer. You’ll need two files: a .service file and a .timer file.

📍 Where to put the files:
System-wide timers: /etc/systemd/system/
User-scoped timers (no root needed): ~/.config/systemd/user/ — manage with systemctl --user

Step 1: Create the Service Unit File

Create /etc/systemd/system/daily-backup.service:

[Unit]
Description=Daily backup script

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh

[Install]
WantedBy=multi-user.target

Type=oneshot is the right choice for scheduled tasks. It tells systemd the process runs once and exits. If you need help writing the script itself, check out my guide on bash script fundamentals.

Step 2: Create the Timer Unit File

Create /etc/systemd/system/daily-backup.timer:

[Unit]
Description=Run daily backup at 2am

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true

[Install]
WantedBy=timers.target

Notice the [Install] section uses WantedBy=timers.target, not multi-user.target. This ensures systemd properly tracks it in the timer subsystem. The matching filename (daily-backup.timer for daily-backup.service) is what links them together.

Step 3: Reload, Enable, and Start

After creating or editing unit files, you need to tell systemd about the changes. For a refresher on the difference between reload and restart, see systemctl restart vs reload.

sudo systemctl daemon-reload
sudo systemctl enable --now daily-backup.timer

The --now flag enables and starts the timer in one command. Your backup script will now fire every day at 2:00 AM. For the full set of systemctl commands, I’ve got a dedicated guide.

OnCalendar Syntax: Schedule Reference

The OnCalendar format follows: DayOfWeek YYYY-MM-DD HH:MM:SS. It’s more verbose than cron syntax, but also more readable. Here are the expressions you’ll use most, as documented in the official systemd.timer man page:

Quick Reference: OnCalendar Expressions

  • daily*-*-* 00:00:00 (midnight every day)
  • weeklyMon *-*-* 00:00:00 (Monday midnight)
  • monthly*-*-01 00:00:00 (first of each month)
  • hourly*-*-* *:00:00 (top of every hour)
  • Mon..Fri *-*-* 09:00:00 → weekdays at 9 AM
  • *-01,04,07,10-01 00:00:00 → quarterly, first day

The real lifesaver here is the ability to test these expressions before you deploy. We’ll get to that in the debugging section below.

Essential Timer Options You Should Know

Beyond OnCalendar, a few directives separate a good timer from a great one.

Persistent=true: Never Miss a Scheduled Run

This is the single most important option most articles skip. When Persistent=true is set, systemd records the last time the timer fired. If the system was powered off when a trigger was due, it fires immediately on next boot.

I learned this the hard way. My homelab backup timer was running faithfully for months until I rebooted my server for a kernel upgrade right at 2 AM. Without Persistent=true, that night’s backup just vanished. Never again.

RandomizedDelaySec: Spread the Load Across Systems

If you manage multiple machines, RandomizedDelaySec= adds a random delay to prevent every system from hitting a shared resource at the exact same moment. Ubuntu’s built-in apt-daily.timer uses this to spread package metadata updates across the day.

[Timer]
OnCalendar=daily
Persistent=true
RandomizedDelaySec=30min

Two other options worth knowing:

  • AccuracySec: Controls how precisely the timer fires. The default is 1 minute (systemd may batch wake-ups to save power). Set AccuracySec=1us if you need precise timing.
  • Unit=: Override the default same-name pairing. Use Unit=other-service.service to trigger a differently named service.

Managing and Debugging Systemd Timers

Setting up timers is half the job. Knowing how to monitor and troubleshoot them is what separates a working system from a reliable one.

List All Active Timers

systemctl list-timers --all

This shows every timer on the system: when it last fired, when it fires next, and which service it triggers. It’s the first command I run when I’m checking on my automatic backups in Linux.

Check Timer Status

systemctl status daily-backup.timer

This shows whether the timer is enabled, active, and its recent trigger history. For a deeper dive into reading service status, check out how to check systemd service status.

Read Logs with journalctl

journalctl -u daily-backup.service

This is where systemd timers truly shine. Every run is logged with full output, exit codes, and timestamps. No more wondering “did my cron job actually run?” For the complete logging toolkit, see my guide on using journalctl to read logs.

Test Calendar Expressions with systemd-analyze

This is the debugging tool most people don’t know about. Before enabling a timer, preview exactly when it will fire:

systemd-analyze calendar 'Mon..Fri *-*-* 09:00:00' --iterations=5

This prints the next 5 trigger times. No more guessing. You can also lint your timer file for syntax errors with systemd-analyze verify. Full details are in the systemd-analyze documentation.

Real-World Systemd Timer Examples

Let’s put it all together with three practical examples you can adapt for your own systems.

Example 1: Daily Log Cleanup

Clean up old log files every day at 3 AM. Pair this with logrotate for log management for a complete log maintenance strategy.

# /etc/systemd/system/log-cleanup.service
[Unit]
Description=Clean old log files

[Service]
Type=oneshot
ExecStart=/usr/local/bin/cleanup-logs.sh
# /etc/systemd/system/log-cleanup.timer
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true

[Install]
WantedBy=timers.target

Example 2: Boot + Recurring Health Check

Run a health check 5 minutes after boot, then every hour. Uses the monotonic combo pattern.

# /etc/systemd/system/healthcheck.timer
[Timer]
OnBootSec=5min
OnUnitActiveSec=1h

[Install]
WantedBy=timers.target

Example 3: Weekly Backup with Random Delay

Run a weekly backup every Sunday at midnight, but add a random delay so multiple servers don’t slam the backup target simultaneously.

# /etc/systemd/system/weekly-backup.timer
[Timer]
OnCalendar=Sun *-*-* 00:00:00
Persistent=true
RandomizedDelaySec=30min

[Install]
WantedBy=timers.target

The .service file follows the same pattern from our step-by-step example above. Refer to the systemd.service unit reference for the full list of service directives.

“systemd-timer is so much better than cron that it was actually quite a lot of fun in the end to have everything converted.” — Anteru (Matthäus G. Chajdas), Graphics Engineer, AMD

Start Automating with Systemd Timers

Knowing how to use systemd timers in Linux gives you better logging, missed-run recovery, overlap protection, and resource controls that cron simply can’t match. The two-file pattern (a .timer plus a .service) takes a few extra minutes to set up, but the reliability payoff is worth it every time.

If you’re coming from a cron background, I’d suggest converting one timer at a time. Start with your most critical scheduled task. Once you see full logging in journalctl and Persistent=true catching a missed run after a reboot, you’ll wonder why you waited so long.

Ready to level up your Linux workflow? Check out more Linux terminal productivity tips, or dive deeper into how to create a systemd service to build more powerful automation chains.

author avatar
Alexa Velinxs
I'm Alexa Velinxs, a cryptocurrency trading expert passionate about demystifying digital assets for both beginners and seasoned investors. Through my writing, I share actionable strategies, market insights, and practical tips to help you navigate the crypto landscape with confidence. Let's explore the future of finance together.
Related Posts