How to Configure Multiple systemd Services to Use a Single Timer: Best Practices and Examples


2 views

When working with systemd timers, a common requirement is triggering multiple services from a single timer. The documentation shows conflicting approaches, leaving many administrators uncertain about the optimal implementation. Let's clarify the correct methodology.

For a single service, the configuration is straightforward:


# /etc/systemd/system/backup.service
[Unit]
Description=Database Backup

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

# /etc/systemd/system/backup.timer
[Unit]
Description=Daily Backup Timer

[Timer]
OnCalendar=daily
Unit=backup.service

[Install]
WantedBy=timers.target

The most reliable method uses a custom target that groups services:


# /etc/systemd/system/daily-tasks.target
[Unit]
Description=Target for daily maintenance tasks
StopWhenUnneeded=yes

# Service files (example for service1)
[Unit]
Description=Service 1
WantedBy=daily-tasks.target

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

# Timer configuration
[Unit]
Description=Daily Tasks Timer

[Timer]
OnCalendar=daily
Unit=daily-tasks.target

[Install]
WantedBy=timers.target

The target-based approach provides several advantages:

  • Explicit dependency management
  • Clean separation of concerns
  • Consistent with systemd's design patterns
  • Easier to maintain and extend

While other approaches exist, they have limitations:


# Problematic alternative (not recommended)
[Timer]
Unit=service1.service  # Only triggers one service

The manual symlink method (creating .wants directories) works but violates the principle of declarative configuration.

For production systems, follow these guidelines:

  1. Always use a dedicated target for multiple services
  2. Keep service units independent of timer activation
  3. Use WantedBy in services rather than manual symlinks
  4. Document the relationship between components

Here's a full implementation for a maintenance system:


# /etc/systemd/system/nightly-maintenance.target
[Unit]
Description=Nightly maintenance tasks
StopWhenUnneeded=yes

# /etc/systemd/system/clean-temp.service
[Unit]
Description=Clean temporary files
WantedBy=nightly-maintenance.target

[Service]
Type=oneshot
ExecStart=/usr/bin/clean-temp.sh

# /etc/systemd/system/rotate-logs.service
[Unit]
Description=Rotate application logs
WantedBy=nightly-maintenance.target

[Service]
Type=oneshot
ExecStart=/usr/bin/logrotate -f /etc/logrotate.d/applications

# /etc/systemd/system/nightly.timer
[Unit]
Description=Nightly maintenance timer

[Timer]
OnCalendar=*-*-* 03:00:00
Unit=nightly-maintenance.target
Persistent=true

[Install]
WantedBy=timers.target

If services aren't triggering as expected:


systemctl list-timers --all
journalctl -u nightly-maintenance.target
systemctl list-dependencies nightly-maintenance.target

When designing systemd timer-service architectures, engineers often need to trigger multiple services from a single timer event. The complexity arises from systemd's dependency management system and the various documented approaches that sometimes conflict.

The most robust solution involves creating a custom target that aggregates multiple services:


# /etc/systemd/system/batch-jobs.target
[Unit]
Description=Target for aggregated batch jobs

Each service should be configured to be "wanted by" our custom target:


# /etc/systemd/system/service1.service
[Unit]
Description=First batch job

[Service]
ExecStart=/usr/local/bin/job1.sh

[Install]
WantedBy=batch-jobs.target

The timer unit activates the target rather than individual services:


# /etc/systemd/system/hourly-jobs.timer
[Unit]
Description=Timer for hourly batch jobs

[Timer]
OnCalendar=hourly
Unit=batch-jobs.target

[Install]
WantedBy=timers.target

The most reliable method combines both Install directives and symlinks:


# After creating units
sudo systemctl enable service1.service
sudo systemctl enable service2.service
sudo mkdir -p /etc/systemd/system/batch-jobs.target.wants
sudo ln -s /etc/systemd/system/service1.service /etc/systemd/system/batch-jobs.target.wants/
sudo ln -s /etc/systemd/system/service2.service /etc/systemd/system/batch-jobs.target.wants/
sudo systemctl enable hourly-jobs.timer

The target unit acts as an abstraction layer that:

  • Decouples the timer from specific service implementations
  • Allows flexible service addition/removal without timer modifications
  • Maintains proper dependency ordering through systemd's native mechanisms

Key verification commands:


systemctl list-dependencies batch-jobs.target
systemctl list-timers --all
journalctl -u hourly-jobs.timer -u batch-jobs.target