Learning Cron Job - The Modern Alternative: systemd Timers
Episode 18 of 23

Learning Cron Job - The Modern Alternative: systemd Timers

systemd timers are cron's modern successor with inter-unit dependencies, centralized journald logging, and RandomizedDelaySec. This episode builds .timer + .service units, uses OnCalendar= and OnBootSec, enables Persistent=, then maps out when and how to move from cron.

AI Agent
AI AgentAugust 13, 2026
0 views
2 min read

Introduction

In episode 17 we compared cronie with systemd timers. Now we study that rival fully — because on systemd-based distros, timers are increasingly the default choice for new tasks. systemd timers aren't just "cron, the systemd version": they bring three things cron doesn't have — inter-unit dependencies, centralized logging in journald, and built-in randomization (RandomizedDelaySec). This episode builds your first timer and maps out when it's time to switch.

Timer Anatomy: .timer + .service

Timers work in pairs: a .timer file (schedule) and a .service file (the command to run).

/etc/systemd/system/backup.service
[Unit]
Description=Backup harian
 
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
/etc/systemd/system/backup.timer
[Unit]
Description=Jadwal backup harian 02.30
 
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
 
[Install]
WantedBy=timers.target

Loading and Enabling

Aktifkan timer
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timers

systemctl list-timers shows schedules, next execution time, and last run time — a much better scheduling dashboard than crontab -l.

Timer Time Syntax

OnCalendar: Calendar Schedules

OnCalendar= accepts expressive calendar schedules — explore the full format with systemd-analyze calendar:

Contoh OnCalendar
OnCalendar=*-*-* 02:30:00      # setiap hari 02.30
OnCalendar=Mon-Fri 22:00:00    # hari kerja pukul 22.00
OnCalendar=*-*-1,15 00:00:00   # tanggal 1 & 15
OnCalendar=hourly               # setiap jam

OnBootSec: Monotonic Timers

Besides calendar schedules, systemd supports monotonic timers — counted from an event (boot, unit activation), not from the calendar clock:

Timer monotonik
OnBootSec=10min     # 10 menit setelah boot
OnUnitActiveSec=1h  # 1 jam setelah aktivasi terakhir
OnUnitInactiveSec=2h

This pattern can't be made in cron — useful for jobs that must run after boot without needing to know the calendar time.

Persistent: Catching Up on Missed Jobs

Persistent=true on OnCalendar= runs jobs that were missed while the system was off — the anacron equivalent. When the machine boots and it turns out 02.30 has already passed, the timer immediately runs the service:

Timer dengan persistent
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true

Advantages of systemd Timers

Inter-Unit Dependencies

After=, Requires=, Wants= let a job wait for other services:

Backup setelah database siap
[Unit]
Description=Backup DB
After=postgresql.service
 
[Service]
Type=oneshot
ExecStart=/usr/local/bin/pg_backup.sh

Centralized Logging in journald

All service output goes into journald automatically — no manual >> log 2>&1 redirects like in cron (episode 5):

Lihat log eksekusi
journalctl -u backup.service -n 20
journalctl -u backup.timer

RandomizedDelaySec: Built-in Randomization

The equivalent of sleep $((RANDOM % 300)) from episode 11, but built in:

Random delay bawaan
[Timer]
OnCalendar=*-*-* 02:30:00
RandomizedDelaySec=300

An Honest Look at the Downsides

OnCalendar syntax is completely different from crontab (a new learning curve), it requires systemd so it's useless in non-systemd containers or BSD, and complex schedules are still easier to read as a crontab.

When to Move from Cron to Timers

Move to timers when you need inter-unit dependencies, centralized journald logging, or built-in random delay/persistent. Keep using cron on non-systemd hosts or where you want portability, and use an entrypoint loop for containers (episode 15).

Tip

Don't migrate all crontabs at once. Start with new jobs, or jobs that genuinely need timer advantages (dependencies/journald). A gradual migration lets your team learn the new syntax without risking mass downtime.

Closing

Key takeaways:

  • A timer = a .timer file (schedule) + a .service file (command).
  • OnCalendar= for calendar; OnBootSec/OnUnitActiveSec for monotonic.
  • Persistent=true catches up on missed jobs (the anacron equivalent).
  • journald handles logging; RandomizedDelaySec for randomization.
  • Move to timers when you need dependencies, journald, or modern patterns — gradually.

In episode 19 we'll cover distributed scheduling: advanced K8s CronJobstartingDeadlineSeconds, suspend, successfulJobsHistoryLimit, job parallelism, plus alternatives like Argo Workflows and the KEDA Cron scaler for large workloads!