Automation10 min read

cron vs systemd timers: Automate Your Linux VPS

On a Linux VPS, automation is not optional: nightly backups, TLS certificate renewal, log cleanup, health checks — these tasks run themselves or they don't run at all. Cron has ruled for decades, but systemd timers offer deeper integration with the service manager. This guide compares both tools, explains when to choose one over the other, and walks you through creating or migrating a systemd timer step by step.

Why automate tasks on a VPS?

A VPS is a server running continuously, often without direct human supervision. That is precisely why automating recurring tasks is essential: nobody will be around at 3 AM to trigger a backup or renew a Let's Encrypt certificate. A forgotten task can mean data loss, an expired certificate taking the site offline, or a disk full because log rotation never ran. The two main tools available on Linux — cron and systemd timers — allow you to schedule these operations reliably.

Typical use cases on a VPS

  • Backups — MySQL or PostgreSQL dumps, file archiving to remote storage
  • TLS certificate renewalcertbot renew or acme.sh --cron scheduled twice a day
  • Log rotation and cleanup — deleting files older than 30 days, weekly compression
  • Health checks — verifying a service is listening, auto-restarting if needed
  • Data synchronizationrsync to a second node, cache refresh
  • Environment cleanup — deleting expired sessions, emptying temporary directories

cron: a quick refresher

Cron is present on virtually every Linux system. To schedule a task, edit the current user's crontab with crontab -e. The syntax uses five space-separated fields: minute, hour, day of month, month, day of week, followed by the command. For example, 0 3 * * * /usr/local/bin/backup.sh runs the script every day at 3 AM. Files placed in /etc/cron.d/ belong to the system and can specify the execution user directly on the line. Cron keeps no native history of past runs and does not handle missed jobs if the machine was off.

systemd timers: an introduction

A systemd timer is a pair of two unit files: a .service file describing what to run, and a .timer file describing when to trigger it. The timer is activated with systemctl enable --now myservice.timer and managed by the service manager, exactly like any other systemd service. The OnCalendar= directive accepts rich syntax: daily, Mon *-*-* 03:00:00, *:0/15 (every 15 minutes). All output goes through journald and is accessible via journalctl -u myservice.service. The command systemctl list-timers shows all active timers, their next deadline and last activation.

cron vs systemd timers: comparison table

Criterioncronsystemd timer
Schedule syntax5-field `* * * * *``OnCalendar=` readable (`daily`, `Mon 03:00`)
LogsMail output or manual redirectNative journald, `journalctl -u`
Missed jobs (machine off)Lost (unless `anacron`)`Persistent=true` replays after reboot
Inter-service dependenciesNone`After=`, `Requires=`, `Wants=`
Isolation and resource limitsInherits shell envCgroups, `MemoryMax=`, `CPUQuota=`
Run as specific userUser field in `/etc/cron.d/``User=` and `Group=` in `.service`
DebuggingHard without logs`systemctl status`, `journalctl -xe`
AvailabilityAll Unix systemsSystems with systemd (Debian, Ubuntu, RHEL…)

When to keep cron?

Cron remains the right choice in several situations. On older or minimal systems without systemd — some containers, BSD, Alpine images — cron is often the only tool available. In a multi-user environment where each user manages their own tasks via crontab -e, cron is simpler to delegate without granting root rights. For very simple one-liner scripts, the crontab stays readable and maintainable without creating two unit files. The deciding criterion is the need for logs, dependencies or resource constraints: if you do not need them, cron does the job perfectly.

When to switch to systemd timers?

Systemd timers become the clear choice as soon as the task goes beyond a simple isolated command. You need journald to trace every execution, its output and return code without manual plumbing? Systemd timers. Your backup script must only run after PostgreSQL is ready (After=postgresql.service)? Systemd timers. You want to cap the memory of a sync job so it does not starve other processes (MemoryMax=512M)? Systemd timers. And if the server restarts at 2:58 AM when the 3 AM backup was due, Persistent=true guarantees it will run at next boot.

Create a systemd timer from scratch (example: daily backup)

01

Create the service file

Open /etc/systemd/system/backup.service and enter: [Unit], Description=Daily PostgreSQL backup, After=postgresql.service, then [Service], Type=oneshot, User=postgres, ExecStart=/usr/local/bin/backup.sh. The oneshot type indicates the service exits after the script completes.

02

Create the timer file

Create /etc/systemd/system/backup.timer with: [Unit], Description=Daily trigger for backup, then [Timer], OnCalendar=*-*-* 03:00:00, Persistent=true, and [Install], WantedBy=timers.target.

03

Reload systemd and enable the timer

Run systemctl daemon-reload so systemd picks up the new files, then systemctl enable --now backup.timer to activate and immediately start the timer.

04

Check the timer

Type systemctl list-timers --all to see your timer in the list with its next and last execution dates. Use systemctl status backup.timer for timer state, and systemctl status backup.service for the result of the last execution.

05

Read the logs

Access all service output with journalctl -u backup.service for the full history, or journalctl -u backup.service -n 50 --since today for the last 50 lines today.

Migrate an existing cron job to systemd

01

Identify the cron job to migrate

List your crontabs with crontab -l (current user) and cat /etc/cron.d/* (system). Note the exact command, the execution user, and the five-field schedule. For example: 30 2 * * 1 root /usr/bin/certbot renew --quiet means every Monday at 2:30 AM as root.

02

Translate the schedule to OnCalendar

The OnCalendar= format reads DayOfWeek Year-Month-Day Hour:Minute:Second. 30 2 * * 1 becomes Mon *-*-* 02:30:00. To test the translation before applying it, use systemd-analyze calendar 'Mon *-*-* 02:30:00' which displays the next 10 calculated occurrences.

03

Create the .service and .timer files

Create /etc/systemd/system/certbot-renew.service with Type=oneshot, ExecStart=/usr/bin/certbot renew --quiet, and User=root. Then create /etc/systemd/system/certbot-renew.timer with OnCalendar=Mon *-*-* 02:30:00 and Persistent=true. Run systemctl daemon-reload && systemctl enable --now certbot-renew.timer.

04

Disable the cron line and validate

Comment out or delete the line in the original crontab. Test immediately with systemctl start certbot-renew.service and check the result via journalctl -u certbot-renew.service -n 20. Verify the timer appears in systemctl list-timers.

For a task that must run a few minutes after boot and then regularly, combine OnBootSec=5min and OnUnitActiveSec=1h in the [Timer] block. The job starts 5 minutes after boot, then every hour from there — without depending on a fixed clock time.

Common systemd timer troubleshooting

Three problems come up frequently when setting up timers. First: the timer is active but never triggers. Check with systemctl list-timers that the NEXT column shows a coherent date, and that systemctl status backup.timer shows active (waiting). A forgotten daemon-reload after modifying a unit file is the most common cause. Second: the service fails silently. Check journalctl -u myservice.service -n 50. Verify that the ExecStart= path is absolute. Third: missed tasks are not replayed after reboot. Make sure Persistent=true is present in the [Timer] block.

Conclusion

Cron and systemd timers coexist without issue on the same server — you do not have to migrate everything at once. Keep cron for your simple tasks and existing user scripts, and adopt systemd timers for new automations that benefit from journald logs, inter-service dependencies or cgroup isolation.

A Linux VPS ready for your automations

Our cloud VPS running Ubuntu and Debian include systemd, journald and the full modern stack. Deploy your timers, scripts and backups on reliable infrastructure, with full root access and responsive support.

Need help?

Browse our help center and FAQ, or write to our team — support in French, English and Arabic.