Pracandy
← Newsroom

Features

systemd Timers Instead of Cron

Logging, dependencies, start limits and what cron cannot tell you when a job fails.

25 September 2026

Racks Amravati Data Center
Photo: PiDatacenters, CC BY-SA 4.0, via Wikimedia Commons

Logging and process isolation

The primary advantage of a systemd timer over cron is that it provides structured, queryable logs and strict process isolation for every execution. Cron entries execute shell commands directly, meaning their output is often lost unless explicitly redirected to a file, and their execution environment is minimal and static. When a job runs under systemd, it becomes a first-class service with its own cgroup, environment variables, and journal entries. This distinction matters on a single VPS where multiple services compete for resources. If a scheduled task hangs or leaks memory, systemd can detect the state change and report it in the journal. Cron simply waits for the exit code, leaving you to guess whether the process actually finished or is still consuming RAM in the background.

Dependency management

Cron jobs run in a vacuum. They do not know if the database is ready, if the network interface is up, or if a prerequisite service has started. You must write your own retry logic or sleep commands to handle these race conditions. Systemd timers, however, attach to services that can declare explicit dependencies. A scheduled job can wait for `postgresql.service` to be active before it starts. This removes the need for fragile shell scripts that check for port availability or process existence. On a box running a shared PostgreSQL database for multiple applications, ensuring the database is fully initialized before a maintenance script runs is critical. With systemd, the dependency graph handles this ordering automatically. If the database fails to start, the timer’s associated service simply does not run, and the failure is recorded in the journal rather than resulting in a cryptic SQL error in a log file.

Start limits and failure handling

Cron has no built-in mechanism to prevent a failing job from running again immediately. If a script crashes due to a transient network error, the next minute’s cron entry will trigger it again, potentially overwhelming a downstream API or filling up disk space with repeated error logs. Systemd allows you to configure `StartLimitIntervalSec` and `StartLimitBurst`. Once a service fails too many times within a specific window, systemd stops attempting to start it. This acts as a circuit breaker. For a static site build or a data sync task, this prevents a cascade of failures that could degrade the performance of other services on the same machine. You can define exactly how many times a job may fail before systemd gives up, providing a level of control that cron lacks entirely.

Practical application

Running scheduled work on a single server requires a clear view of what is happening at any given moment. Cron’s opacity makes debugging difficult when a job fails at 3 AM. Systemd’s journal provides a unified log stream where you can filter by unit name, priority, and time. You can see the exact environment variables used, the exit code, and any standard output or error messages. This transparency is essential when maintaining multiple distinct products from one infrastructure. Whether it is a database backup, a cache clear, or a static asset rebuild, knowing precisely why a job succeeded or failed reduces the time spent hunting for issues. The shift from cron to systemd timers is not just about syntax; it is about treating scheduled tasks as managed services rather than detached shell commands. This approach aligns with how the rest of the system is managed, ensuring that every component, from the web server to the background jobs, reports its status in a consistent and inspectable way. See how this level of attention to detail applies to our other tools, such as Invoiceful, where reliability is paramount.