Why I Left Cron#
I’ve been using cron on my servers for years. It’s simple, reliable, and it works. But recently I discovered systemd timers and I’m not going back. The main reason: integrated logs in journald, no .log files scattered around the system, and better control over what happens when the server starts or reboots.
In my specific case, I had a backup that wouldn’t run if the server was off at the scheduled time. With cron, it simply got lost. With systemd timers and Persistent=true, the task runs as soon as the server boots up.
Basic structure: .service + .timer#
Systemd needs two files:
The service (/etc/systemd/system/mibackup.service):
[Unit]
Description=Backup diario de datos
After=network.target
[Service]
Type=oneshot
User=backup
ExecStart=/usr/local/bin/backup.sh
StandardOutput=journal
StandardError=journalThe timer (/etc/systemd/system/mibackup.timer):
[Unit]
Description=Ejecuta backup diariamente
[Timer]
OnCalendar=daily
OnCalendar=*-*-* 03:00:00
Persistent=true
Accuracy=1min
[Install]
WantedBy=timers.targetThen:
sudo systemctl daemon-reload
sudo systemctl enable --now mibackup.timerOnCalendar: the syntax you need#
OnCalendar is systemd’s cron, but more readable:
daily→ every day at midnightweekly→ every Monday at midnighthourly→ every hour*-*-* 03:00:00→ every day at 3 AMMon *-*-* 14:30:00→ every Monday at 14:30*-01,04,07,10-01 00:00:00→ first day of every quarter
You can combine multiple OnCalendar lines:
OnCalendar=*-*-* 03:00:00
OnCalendar=*-*-* 15:00:00This runs the task twice a day.
Persistent=true: the change that convinced me#
By default, if your server is off when a task is scheduled, systemd simply ignores it. With Persistent=true, systemd remembers and runs the task the next time it boots.
On my home server, this is critical. It’s not always on, and I need to guarantee that my backups run even if hours have passed.
[Timer]
OnCalendar=daily
Persistent=trueType=oneshot: for tasks that finish#
The Type=oneshot parameter in the .service indicates that the process will terminate. It’s normal for backup scripts, synchronization, etc.
If you use Type=simple (the default), systemd expects the process to keep running. That’s not what we want here.
View logs without external files#
Here’s the best part: forget about >> /var/log/mibackup.log.
Logs go straight to journald:
# Ver los últimos logs del timer
journalctl -u mibackup.service -n 50
# Ver en tiempo real
journalctl -u mibackup.service -f
# Los últimos 2 horas
journalctl -u mibackup.service --since "2 hours ago"
# Con niveles de prioridad
journalctl -u mibackup.service -p errInside your script you can log with echo or logger:
#!/bin/bash
logger "Iniciando backup..."
/backup/script.sh
logger "Backup completado"Everything is captured automatically.
Checklist of what we did#
- ✅ You created a
.servicewithType=oneshot - ✅ You created a
.timerwithOnCalendarandPersistent=true - ✅ You reloaded systemd and activated the timer
- ✅ You verify logs with
journalctl, no extra log files
Final tip#
Before relying on a timer, test it manually:
# Ejecutar el servicio ahora
sudo systemctl start mibackup.service
# Ver qué pasó
journalctl -u mibackup.service -n 20That’s it. Systemd timers isn’t a panacea, but for scheduled tasks on modern servers, it’s superior to cron in almost every way. Centralized logs in journald, combined with Persistent=true, make it impossible not to recommend it.
On my home server, all backups, cache cleanups, and data synchronizations use timers. Zero loose log files. Everything integrated.
Recommended Equipment#
- Raspberry Pi 3 B+ — Lightweight, low-power server to start your homelab
- Intel N100 Mini PC — Silent and efficient mini PC for 24/7 home server
Affiliate links. No extra cost for you.