CRON VS SYSTEMD

Crontab vs
Systemd Timers

Full comparison with real examples — when to use crontab, when to use systemd timers, how to convert between them, and a side-by-side syntax reference for common scheduling patterns.

Quick Answer: When to Use Each

Use crontab when…Use systemd timers when…
You need quick setup with minimal filesYou need missed runs to fire after reboot (Persistent=true)
The script runs on multiple Unix/Linux systemsYou want logging via journalctl without extra config
You're working in a container or non-systemd systemThe job should only run after another service is ready
You just need a per-user job without rootYou need precise resource limits (CPU, memory) on the job
The team is more familiar with cronYou want to test the schedule with systemd-analyze calendar

Side-by-Side: Cron vs Systemd Timer

Featurecrontabsystemd timer
Setup complexityLow — one fileMedium — two files (.timer + .service)
LoggingEmail or log file (manual setup)Automatic via journalctl
Missed run recovery❌ No (job skipped if machine was off)✅ Yes (Persistent=true)
Dependency management❌ NoneAfter=network.target, etc.
Per-user jobs (no root)✅ Yes (crontab -e)✅ Yes (user systemd units)
Resource limits❌ NoneCPUQuota, MemoryMax, etc.
Randomized delay❌ Not built-inRandomizedDelaySec
Dry-run / testManual (run script, check date)systemd-analyze calendar "..."
Portability✅ Any Unix/Linuxsystemd-based Linux only
Container support✅ Works in containers❌ Not for containers (no systemd in containers)

Converting Cron Expressions to Systemd OnCalendar

Systemd timers use OnCalendar= in the [Timer] section. The syntax is different but more readable than cron's 5-field format.

ScheduleCron expressionSystemd OnCalendar
Every minute* * * * **:*:00
Every 5 minutes*/5 * * * **:0/5:00
Every hour0 * * * *hourly or *:00:00
Every day at midnight0 0 * * *daily or *-*-* 00:00:00
Every day at 2:30 AM30 2 * * **-*-* 02:30:00
Weekdays at 9 AM0 9 * * 1-5Mon..Fri *-*-* 09:00:00
Every Monday at 8 AM0 8 * * 1Mon *-*-* 08:00:00
1st of month at midnight0 0 1 * **-*-01 00:00:00
Every week (Sunday)0 0 * * 0weekly or Sun *-*-* 00:00:00
On boot@rebootOnBootSec=0 (different section)

Crontab: Full Example

# /etc/cron.d/backup-job or crontab -e
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=""

# Daily database backup at 2 AM
0 2 * * *  root  /usr/local/bin/db-backup.sh >> /var/log/db-backup.log 2>&1

Systemd Timer: Equivalent Setup

A systemd timer requires two files: a .service unit (what to run) and a .timer unit (when to run it).

Step 1: Service unit — /etc/systemd/system/db-backup.service

[Unit]
Description=Daily Database Backup
After=network.target

[Service]
Type=oneshot
User=root
ExecStart=/usr/local/bin/db-backup.sh
StandardOutput=journal
StandardError=journal

Step 2: Timer unit — /etc/systemd/system/db-backup.timer

[Unit]
Description=Run db-backup daily at 2 AM

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true         # Run after reboot if missed
RandomizedDelaySec=300  # Add up to 5min random delay

[Install]
WantedBy=timers.target

Step 3: Enable and start the timer

sudo systemctl daemon-reload
sudo systemctl enable db-backup.timer
sudo systemctl start db-backup.timer

# Check status
systemctl status db-backup.timer
systemctl list-timers --all

Check logs

# All output from the job — no log file setup needed
journalctl -u db-backup.service

# Follow logs in real time
journalctl -u db-backup.service -f

# Logs from the last run only
journalctl -u db-backup.service --since "1 day ago"

Missed Run Recovery with Persistent=true

This is one of systemd timers' biggest advantages over cron. With Persistent=true, systemd records the last time the timer fired. If the system was powered off during a scheduled run, the timer fires once immediately on the next boot.

[Timer]
OnCalendar=daily
Persistent=true   # Fire on boot if last run was missed

This is critical for jobs like nightly backups on machines that aren't always on (laptops, development servers). With cron, if the machine is off at 2 AM, the backup simply never runs. With a persistent systemd timer, it runs as soon as you boot the next morning.


Testing Systemd Calendar Expressions

Systemd ships with a built-in tool to validate calendar expressions and preview the next run times:

# Test a calendar expression
systemd-analyze calendar "Mon..Fri *-*-* 09:00:00"

# Output:
#   Original form: Mon..Fri *-*-* 09:00:00
# Normalized form: Mon..Fri *-*-* 09:00:00
#     Next elapse: Mon 2026-05-04 09:00:00 IST
#        (in UTC): Mon 2026-05-04 03:30:00 UTC
#       From now: 3 days 21h left

# Test @shortcuts
systemd-analyze calendar daily
systemd-analyze calendar weekly
Tip: systemd-analyze calendar is the systemd equivalent of a cron tester. Use it before enabling a timer to confirm it fires when you expect.

Migrating from Crontab to Systemd Timers — Practical Guide

If you are running a modern Linux distribution (Ubuntu 20.04+, Debian 11+, RHEL 8+) and want to migrate an existing crontab to systemd, here is the recommended workflow:

Step 1: Audit your current crontab

Run crontab -l and cat /etc/cron.d/* to list all scheduled jobs. Group them by owner and frequency. Jobs that need dependency management, restart policies, or logging to the journal are the best candidates to migrate first.

Step 2: Convert the schedule expression

Use systemd-analyze calendar "Mon *-*-* 04:00:00" to validate the OnCalendar value before writing the timer unit. The --iterations=5 flag shows the next 5 matching times — equivalent to using CronRead's next-run-times feature for cron expressions. See the conversion table above for cron-to-OnCalendar mappings.

Step 3: Decide on Persistent=true

If the job must not be missed (database backups, billing jobs, compliance reports), set Persistent=true in the timer unit. This instructs systemd to trigger the service immediately on boot if the last scheduled run was missed — for example, because the server was down. Standard cron silently skips missed runs with no recovery.

Step 4: Verify and monitor

After enabling the timer with systemctl enable --now my-job.timer, verify it with systemctl list-timers --all to confirm the next trigger time. Use journalctl -u my-job.service -f to follow live output. Systemd automatically logs start time, exit code, and duration to the journal — no manual log redirection needed.

When to Keep Crontab Instead of Migrating

Systemd timers are more powerful, but crontab is still the right choice in several common situations:


Frequently Asked Questions

Is systemd replacing cron on modern Linux?
Systemd timers are increasingly preferred on systems that already use systemd (most modern Debian/Ubuntu/Fedora/RHEL systems). However, cron is still widely used, ships by default, and is not going anywhere. Many sysadmins use both — cron for personal scripts and systemd timers for system services.
Do I need root to create a systemd timer?
No. User-level timers go in ~/.config/systemd/user/ and are managed with systemctl --user. System-level timers (for all users, run as root or a service account) go in /etc/systemd/system/ and require root.
Can I use systemd timers in Docker?
No — systemd requires the init system, which Docker containers don't run. For container scheduling, use supercronic or ofelia inside Docker, or Kubernetes CronJobs if you're on Kubernetes.
How do I list all active systemd timers?
Run systemctl list-timers to see all active timers and their next fire times. Add --all to include inactive timers.

Related Guides

GuideWhat you'll find
Linux Crontab Examples30+ crontab examples, @shortcuts, output logging
Docker Cron Job Examplessupercronic, ofelia, Dockerfile cron patterns
Kubernetes CronJob ExamplesK8s CronJob YAML, concurrencyPolicy, history
Cron Expression TesterValidate any cron expression online for free
Cron Syntax GuideFull reference for all cron fields and operators
⚡ Validate Your Cron Expression

🔧 Other Free Developer Tools

⏱ Cron Generator 🕐 Timestamp Converter 🔑 JWT Decoder 📄 YAML ↔ JSON 🔤 Base64 Encoder 🗄 SQL Explainer