๐ŸŽ New User? Get 20% off your first purchase with code NEWUSER20 ยท โšก Instant download ยท ๐Ÿ”’ Secure checkout Register Now โ†’
Menu

Categories

Systemd Timers: Modern Linux Scheduling Beyond Cron Jobs

Systemd Timers: Modern Linux Scheduling Beyond Cron Jobs

Cron has scheduled Linux jobs since 1975, but systemd timers offer features cron simply cannot match: integrated logging, dependency ordering, randomized jitter, persistent catch-up after downtime, calendar expressions with second precision, and per-job resource limits via cgroups. If you administer modern Linux, replacing cron with timers pays for itself the first time a job needs to retry, log structured output, or wait for the network.

Anatomy of a timer unit

A systemd timer is two files: a .service describing the work, and a .timer describing when. Both live in /etc/systemd/system/ for system-wide jobs or ~/.config/systemd/user/ for per-user jobs.

# /etc/systemd/system/backup.service
[Unit]
Description=Nightly database backup
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
User=postgres
ExecStart=/usr/local/bin/backup-db.sh
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=7
# /etc/systemd/system/backup.timer
[Unit]
Description=Run nightly database backup

[Timer]
OnCalendar=*-*-* 02:30:00
RandomizedDelaySec=15min
Persistent=true
AccuracySec=1min

[Install]
WantedBy=timers.target

Enable and start with systemctl enable --now backup.timer. Check status with systemctl list-timers, which shows the next/previous run for every active timer.

Calendar expressions you will actually use

OnCalendar= accepts a powerful syntax. Common patterns:

  • OnCalendar=hourly โ€” every hour on the hour.
  • OnCalendar=*-*-* 03:00:00 โ€” every day at 03:00 local time.
  • OnCalendar=Mon..Fri *-*-* 09:00:00 โ€” weekday mornings only.
  • OnCalendar=*:0/15 โ€” every 15 minutes.
  • OnCalendar=monthly โ€” first of the month at midnight.

Test any expression before deploying:

systemd-analyze calendar 'Mon..Fri *-*-* 09:00:00'
systemd-analyze calendar --iterations=5 weekly

Persistence and jitter

Two timer options eliminate the largest cron pain points:

  • Persistent=true โ€” if the system was off when the timer should have fired, run it as soon as the system boots. No more "the laptop was sleeping, so today's backup never happened."
  • RandomizedDelaySec=15min โ€” adds a per-host random delay. When 200 servers all run an apt-update timer, this avoids a thundering herd against your mirror.

Logging and observability

Every invocation is captured by the journal. To inspect:

journalctl -u backup.service --since '24 hours ago'
journalctl -u backup.service -p err -b           # errors since last boot
systemctl status backup.timer                    # last/next, exit code

Cron jobs traditionally email output via local MTA โ€” easy to miss when nobody reads root@localhost. Timers integrate with rsyslog, journald, and any log shipper you already run.

Resource limits and isolation

Because the work runs as a transient service, you get the full systemd sandboxing arsenal for free:

[Service]
MemoryMax=512M
CPUQuota=20%
PrivateTmp=true
ProtectSystem=strict
NoNewPrivileges=true
ReadWritePaths=/var/backups

A runaway backup script can no longer OOM-kill your database; a compromised cron command can no longer write outside its declared paths.

Migrating from crontab

For each cron line, generate a service+timer pair. A simple translation table:

  • 0 3 * * * โ†’ OnCalendar=*-*-* 03:00:00
  • */10 * * * * โ†’ OnCalendar=*:0/10
  • @reboot โ†’ OnBootSec=1min in the timer.

Use systemd-run --on-calendar='*:0/5' --unit=test /usr/bin/date to prototype a one-off timer without writing files.

Best practices

  • Always pair timers with Type=oneshot services unless the script daemonizes itself.
  • Set AccuracySec= to give systemd permission to batch wake-ups (saves laptop battery, reduces I/O contention on busy hosts).
  • Use OnUnitActiveSec= for "every N minutes after the last run finished" semantics.
  • Add Wants=network-online.target for jobs that need DNS or remote endpoints.
  • Pin a sane WorkingDirectory= and absolute ExecStart= path; do not rely on PATH.

Cron will keep working forever, but every new scheduled job you write should be a systemd timer. The boilerplate is two files, the operational payoff is observability, isolation, and reliability you cannot retrofit into crontab.

Share this article:
Dargslan Editorial Team (Dargslan)
About the Author

Dargslan Editorial Team (Dargslan)

Collective of Software Developers, System Administrators, DevOps Engineers, and IT Authors

Dargslan is an independent technology publishing collective formed by experienced software developers, system administrators, and IT specialists.

The Dargslan editorial team works collaboratively to create practical, hands-on technology books focused on real-world use cases. Each publication is developed, reviewed, and...

Programming Languages Linux Administration Web Development Cybersecurity Networking

Stay Updated

Subscribe to our newsletter for the latest tutorials, tips, and exclusive offers.