How to Create and Manage Systemd Timer Units

Create, enable, monitor, and troubleshoot systemd timer units with practical `.timer`, `.service`, `systemctl`, and `journalctl` examples.

How to Create and Manage Systemd Timer Units

Systemd timer units schedule work on Linux without relying on cron. If you need your server to run a backup, cleanup job, health check, or report at a predictable time, a systemd timer gives you scheduling, service isolation, dependency handling, and logs in one place.

The key idea is simple: the .timer file says when to run, and the .service file says what to run. That split makes timers easy to inspect with systemctl and easy to debug with journalctl.

Understanding Systemd Timer Unit Structure

A systemd timer unit is always paired with a corresponding service unit (or another unit type) that it is intended to activate. The timer unit itself defines when the associated unit should be activated, while the service unit defines what action to perform. Both units typically reside in the same directory, often /etc/systemd/system/ for custom units.

A typical timer unit file has the .timer extension, and its associated service unit file has the .service extension. For example, if you want to schedule a task defined in mytask.service, you would create a mytask.timer file.

Example: mytask.timer

[Unit]
Description=Run my custom task daily

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target

Let's break down the key sections:

  • [Unit] Section:

    • Description: A human-readable description of the timer. This is helpful for identification in status outputs.
  • [Timer] Section: This is the core of the timer unit, defining the scheduling.

    • OnCalendar=daily: This directive specifies when the timer should activate. daily is a shorthand for midnight each day. Other examples include:
      • hourly: Every hour.
      • weekly: Every week (equivalent to Mon *-*-* 00:00:00).
      • Sun *-*-* 10:00: Every Sunday at 10 AM.
      • *-*-15 14:30: On the 15th of every month at 2:30 PM.
      • Mon..Fri *-*-* 09:00: Weekdays at 9 AM.
    • Persistent=true: If this is set to true, the timer will activate as soon as possible if the event occurred while the system was off. For OnCalendar timers, this means if the system was off during the scheduled time, the timer will trigger once the system boots up and the timer becomes active.
    • OnBootSec=: Activates the timer a specified time after the system boots. For example, OnBootSec=15min would trigger 15 minutes after boot.
    • OnUnitActiveSec=: Activates the timer a specified time after the unit it activates last became active. For example, OnUnitActiveSec=1h schedules another run one hour after the associated service was last activated.
    • OnUnitInactiveSec=: Activates the timer a specified time after the unit it activates last became inactive.
    • AccuracySec=: Specifies the timer's accuracy. Systemd tries to wake up the system for timers only if the event is within this time window, helping to save power. Defaults to 1min.
    • RandomizedDelaySec=: Adds a random delay to the timer trigger, up to the specified duration. Useful for distributing load.
  • [Install] Section: This section defines how the timer unit can be enabled.

    • WantedBy=timers.target: This directive ensures that when the timer is enabled, it becomes part of the timers.target, which is a standard target that includes all active timers. This means the timer will start automatically on boot once enabled.

Example: mytask.service

[Unit]
Description=My custom task service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/my_custom_script.sh
User=myuser
Group=mygroup

[Install]
WantedBy=multi-user.target
  • [Unit] Section:

    • Description: A description of the service.
  • [Service] Section: This defines the service itself.

    • Type=oneshot: Suitable for tasks that run once and then exit. Other types exist for long-running daemons.
    • ExecStart: The command to execute. Ensure the script has execute permissions.
    • User/Group: Specifies the user and group under which the command should run. It's good practice not to run tasks as root unless absolutely necessary.
  • [Install] Section: This section is usually not needed for a oneshot service that should only be started by a timer. Enable the timer, not the service.

Creating and Enabling Timer Units

Follow these steps to create and manage your systemd timer units:

  1. Create the Service Unit File: Define your task in a .service file. Place it in /etc/systemd/system/ (or ~/.config/systemd/user/ for user-specific timers).

    sudo nano /etc/systemd/system/mytask.service
    

    Paste the example service content above and save.

  2. Create the Timer Unit File: Define the schedule in a corresponding .timer file. Place it in the same directory as the service file.

    sudo nano /etc/systemd/system/mytask.timer
    

    Paste the example timer content above and save.

  3. Reload Systemd Daemon: After creating or modifying unit files, you need to tell systemd to reload its configuration.

    sudo systemctl daemon-reload
    
  4. Enable the Timer: To make the timer start automatically on boot, enable it.

    sudo systemctl enable mytask.timer
    

    Note: You do NOT enable the service file if it's solely meant to be triggered by the timer.

  5. Start the Timer: Start the timer immediately. It will then run according to its schedule.

    sudo systemctl start mytask.timer
    

Managing and Monitoring Timer Units

Systemd provides several systemctl commands to manage and monitor your timers:

  • List all timers: See all active and inactive timers.

    systemctl list-timers
    

    This command provides output like:

    NEXT                        LEFT        LAST        PASSED      UNIT          ACTIVATES
    Tue 2023-10-27 08:00:00 UTC 10h left    Wed 2023-10-26 08:00:00 UTC 14h ago       mytask.timer  mytask.service
    

    This shows when the timer is scheduled to run next, how long until it runs, when it last ran, and which service it activates.

  • List timers for a specific unit: If you want to see timers related to a specific service.

    systemctl list-timers --all | grep mytask.service
    
  • Check Timer Status: Get detailed information about a specific timer.

    systemctl status mytask.timer
    

    This will show if the timer is active, when it's scheduled to run next, and recent log entries.

  • View Service Logs: To see the output and status of the task executed by the timer, check the associated service's logs.

    journalctl -u mytask.service
    

    You can also follow the logs in real-time:

    journalctl -f -u mytask.service
    
  • Stop a Timer: If you need to temporarily disable a timer.

    sudo systemctl stop mytask.timer
    
  • Disable a Timer: To prevent a timer from starting on boot and stop it if it is running.

    sudo systemctl disable --now mytask.timer
    

Advanced Timer Configurations

Setting Specific Intervals

Instead of daily or hourly, you can define more precise intervals:

  • Every N minutes: OnUnitActiveSec=15min (runs 15 minutes after the service was last activated).
  • Specific times: OnCalendar=*-*-* 02:30:00 (runs daily at 2:30 AM).
  • Combining conditions: OnCalendar=Mon..Fri *-*-* 08:00:00 (runs weekdays at 8 AM).

Using AccuracySec for Power Saving

If your task doesn't need to run at an exact moment, consider using AccuracySec. For example, AccuracySec=5min tells systemd it's okay to wake the system up within 5 minutes of the scheduled time. This allows systemd to bundle timer events and potentially keep the system in a lower power state for longer.

[Timer]
OnCalendar=hourly
AccuracySec=5min

Persistent vs. WakeSystem

  • Persistent=true ensures that if an OnCalendar event is missed due to the system being off, it will run once upon the next boot. This is crucial for tasks that must not be skipped.
  • WakeSystem=true asks systemd to wake the system from suspend for the timer if the system and hardware support it. It is not the same as deciding whether the machine is on AC power or battery.

Timers vs. Cron

Feature Systemd Timers Cron
Integration Deep integration with systemd services, targets Standalone utility
Scheduling Flexible (calendar, relative, boot-based) Primarily time-based expressions
Logging Centralized via journalctl Scattered (/var/log/syslog, /var/log/cron.log)
Error Handling Can tie actions to service failures Basic mail notifications
Dependencies Can depend on other services being active Limited
Execution Can run as specific users, groups Can run as specific users via crontab
Power Mgmt Can be optimized for power saving (AccuracySec) Less direct control

When to choose Systemd Timers:

  • When you need tighter integration with other systemd services.
  • When centralized logging and easier debugging are priorities.
  • When you require more advanced scheduling options (e.g., time since last run).
  • For tasks related to system state or power management.

When Cron might still be preferred:

  • For very simple, standalone tasks on systems not fully embracing systemd.
  • For maximum compatibility across different Linux distributions and older systems.

Troubleshooting Common Issues

  • Task not running:
    • Check timer status: systemctl status mytask.timer. Look for Active: active and Triggered... messages.
    • Check service logs: journalctl -u mytask.service. Ensure the script is executable and has no errors.
    • Verify OnCalendar syntax: Use systemd-analyze calendar 'your-calendar-string' to test.
    • Ensure the timer is enabled and started: systemctl list-timers --all.
  • Task runs too early/late:
    • Check AccuracySec and RandomizedDelaySec.
    • Ensure the system clock is accurate (timedatectl status).
  • Permission errors:
    • Confirm the User and Group specified in the .service file have the necessary permissions for the script and any files it accesses.
    • If no user is specified, it defaults to root. Be cautious with root privileges.

Takeaway

Use a systemd timer when the job belongs to the machine's service layer: backups, cleanup tasks, monitoring checks, certificate renewal hooks, and other operational work. Create the service first, create the timer next, run systemctl daemon-reload, enable and start the timer, then verify it with systemctl list-timers --all and journalctl -u your.service.