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.dailyis a shorthand for midnight each day. Other examples include:hourly: Every hour.weekly: Every week (equivalent toMon *-*-* 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 totrue, the timer will activate as soon as possible if the event occurred while the system was off. ForOnCalendartimers, 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=15minwould trigger 15 minutes after boot.OnUnitActiveSec=: Activates the timer a specified time after the unit it activates last became active. For example,OnUnitActiveSec=1hschedules 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 to1min.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 thetimers.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:
Create the Service Unit File: Define your task in a
.servicefile. Place it in/etc/systemd/system/(or~/.config/systemd/user/for user-specific timers).sudo nano /etc/systemd/system/mytask.servicePaste the example service content above and save.
Create the Timer Unit File: Define the schedule in a corresponding
.timerfile. Place it in the same directory as the service file.sudo nano /etc/systemd/system/mytask.timerPaste the example timer content above and save.
Reload Systemd Daemon: After creating or modifying unit files, you need to tell systemd to reload its configuration.
sudo systemctl daemon-reloadEnable the Timer: To make the timer start automatically on boot, enable it.
sudo systemctl enable mytask.timerNote: You do NOT enable the service file if it's solely meant to be triggered by the timer.
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-timersThis 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.serviceThis 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.serviceCheck Timer Status: Get detailed information about a specific timer.
systemctl status mytask.timerThis 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.serviceYou can also follow the logs in real-time:
journalctl -f -u mytask.serviceStop a Timer: If you need to temporarily disable a timer.
sudo systemctl stop mytask.timerDisable 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=trueensures that if anOnCalendarevent 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=trueasks 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 forActive: activeandTriggered...messages. - Check service logs:
journalctl -u mytask.service. Ensure the script is executable and has no errors. - Verify
OnCalendarsyntax: Usesystemd-analyze calendar 'your-calendar-string'to test. - Ensure the timer is enabled and started:
systemctl list-timers --all.
- Check timer status:
- Task runs too early/late:
- Check
AccuracySecandRandomizedDelaySec. - Ensure the system clock is accurate (
timedatectl status).
- Check
- Permission errors:
- Confirm the
UserandGroupspecified in the.servicefile 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.
- Confirm the
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.