Systemd controls execution timing through dependency ordering, timer units, and sleep directives. Use After= and Requires= to sequence services, OnCalendar= for precise schedules, and ExecStartPre=sleep= for simple delays. This approach ensures reliable startup order and avoids race conditions in Linux service management.
Understanding Systemd Dependency Ordering
The most common need for controlling timing is ensuring one service starts after another has completed initialization. Systemd uses a directed acyclic graph to manage these dependencies. When you define an order, you are telling systemd that Service B cannot start until Service A has reached its specified target state. This is not a time-based delay but a logical dependency check. To enforce this, use theAfter= directive in your .service file. For example, if your application depends on a database, you should write:
[Unit]
After=postgresql.service
Requires=postgresql.service
The After= directive ensures ordering. If both services are enabled, PostgreSQL will start before your app. The Requires= directive ensures that if PostgreSQL fails to start, your service also fails. This prevents your application from running without its critical backend. Without Requires=, systemd might start your service even if the database is unavailable, leading to runtime errors.
Note that Before= is the inverse of After=. If you specify After=foo.service, it is equivalent to Before=foo.service in the other unit. Always pair ordering with requirement directives to maintain system stability. See how to handle shutdown sequences in Preserve Order of Closing Services While Shutting Down Linux to ensure clean teardowns.
Using Timer Units for Scheduled Execution
For tasks that need to run at specific times or intervals, systemd provides.timer units. These are analogous to cron jobs but offer better integration with the systemd ecosystem. A timer unit activates a service unit based on calendar events or monotonic time.
Create a .timer file alongside your .service file. The timer defines when to trigger the service.
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.target
The OnCalendar= field uses a standard calendar specification. In the example above, the service runs daily at 2:00 AM. The Persistent=true option ensures that if the system was off during the scheduled time, the job runs immediately upon boot. This prevents missed executions.
Enable the timer with systemctl enable --now myjob.timer. The service itself should be disabled (systemctl disable myjob.service) so it only runs via the timer. This separation of concerns makes debugging easier. You can check the next activation time with systemctl list-timers.
If you encounter issues enabling units, refer to Failed to Enable Unit: Invalid Argument systemd-enable.service - Desktop Script Issue for troubleshooting common syntax errors.
Implementing Delays with Sleep Directives
Sometimes, you need a simple delay within a service execution, such as waiting for a network interface to become fully available. You can achieve this using theExecStartPre= directive with a sleep command.
[Service]
ExecStartPre=/bin/sleep 5
ExecStart=/usr/bin/myapp
This inserts a five-second pause before the main application starts. While effective for short waits, this method is not recommended for long delays because it blocks the service startup process. It consumes a PID and holds up the boot sequence unnecessarily.
For longer waits, consider using ConditionPathExists= or custom scripts that poll for resource availability. Avoid chaining multiple sleep commands, as they accumulate and make boot times unpredictable. Instead, refine your dependency graph to remove the need for arbitrary delays.
Handling Environment and Path Timing
Timing issues often stem from environment variables not being ready when the service starts. Systemd loads environment files before executing the service, but external resources might still be initializing. Ensure that all paths in your.service file are absolute. Relative paths can cause failures if the working directory is not set correctly.
Use EnvironmentFile= to load variables from a dedicated file. This keeps your service file clean and allows you to update credentials without modifying the unit file. Restart the daemon after changes with systemctl daemon-reload.
If your script works manually but fails in systemd, check the execution context. See Linux RHEL 7 - Shell Script - Can execute command separately but cannot execute inside a script for details on why manual execution differs from systemd execution.
Frequently Asked Questions
Why does my service start before the database even with After=?
You must also useRequires= or Wants= to establish a hard link between units. After= only defines order; it does not guarantee the dependency exists. Without a requirement directive, systemd may ignore the ordering if the dependency is not explicitly linked.
Can I use cron instead of systemd timers?
Yes, but systemd timers are preferred for modern Linux systems. They integrate with journal logging, support persistent execution, and restart automatically on boot. Cron jobs do not track status in systemctl and lack built-in persistence for missed runs.How do I debug a service that starts too early?
Check the boot log withjournalctl -b -u yourservice.service. Look for messages indicating missing dependencies or failed prerequisites. Verify your After= and Requires= lines match the actual service names. Use systemctl list-dependencies to visualize the startup order.
What happens if a required service fails to start?
If you useRequires=, your service will fail to start if the dependency fails. If you use Wants=, your service will start regardless, but the dependency failure will be logged. Choose Requires= for critical dependencies where the application cannot function without the other service.