How to replace a cron job with a systemd timer

Migrate a cron job to a systemd timer unit for better logging, dependency management, and scheduling.

How to replace a cron job with a systemd timer

Migrate a cron job to a systemd timer unit for better logging, dependency management, and scheduling.

Prerequisites

  • Root or sudo access on a Linux system running systemd.
  • An existing cron job or script to migrate.

Step-by-Step: Replace a Cron Job with a systemd Timer

  1. Create a service unit that defines what to run. systemd timers activate a matching .service unit. Create /etc/systemd/system/backup.service:

    [Unit]
    Description=Daily backup script
    
    [Service]
    Type=oneshot
    ExecStart=/usr/local/bin/backup.sh

    The Type=oneshot directive tells systemd this is a run-and-exit task, not a long-running daemon.

  2. Create a timer unit that defines when to run. Create /etc/systemd/system/backup.timer:

    [Unit]
    Description=Run backup daily at 2:00 AM
    
    [Timer]
    OnCalendar=*-*-* 02:00:00
    Persistent=true
    RandomizedDelaySec=15min
    
    [Install]
    WantedBy=timers.target

    OnCalendar accepts systemd calendar syntax ( daily, weekly, or explicit timestamps). Persistent=true runs the timer immediately if the system was off during the scheduled time. RandomizedDelaySec staggers execution to avoid resource contention.

  3. Reload systemd and enable the timer:

    sudo systemctl daemon-reload
    sudo systemctl enable --now backup.timer

    Enable the .timer file, not the .service file. The timer activates the service automatically.

How to Verify the Timer Is Scheduled

systemctl lists all active timers with their next and last trigger times:

systemctl list-timers --all
NEXT                         LEFT          LAST                         PASSED    UNIT
Tue 2026-03-31 02:00:00 UTC  15h left      Mon 2026-03-30 02:00:00 UTC  9h ago   backup.timer

Common Issues When Migrating Cron to systemd Timers

The timer may not fire if WantedBy=timers.target is missing from the [Install] section. Without this directive, systemctl enable has no target to link the timer to.

Cron environment variables ( PATH, SHELL, MAILTO) do not carry over to systemd services. Set environment variables explicitly in the [Service] section with Environment= or EnvironmentFile=.