Skip to content
ADMIN
FUTURE
Главная страница » systemd Timers Instead of cron: the Complete Guide
FIELD NOTES / 28.09.2026

systemd Timers Instead of cron: the Complete Guide

Maksim Shapovalov6 min read
Сравнение systemd timers и cron: зависимости и журналирование

Why your crontab -e is the past

For decades, cron was the de facto standard for scheduling tasks on Linux. It’s simple, reliable, and… that’s about it. In modern, complex infrastructure, the cron approach is a “blind” launch.

If a cron job fails, you find out when something “breaks.” Logging? Only via syslog or MAILTO. Dependencies? You can’t tell cron: “Hey, only run this backup script after the network comes up and the remote drive is mounted.” Resource management? Forget it.

An “admin” uses cron because “that’s how it’s always been done.” An “architect” uses systemd, because they need control, logging, and dependency management.

systemd isn’t just an init system (a replacement for init.d). It’s a framework for managing the entire operating system. And its timers are a direct, native, and dramatically more powerful successor to cron.

This guide shows you hands-on how to migrate your cron jobs to systemd timers, and why you’ll never want to go back.


Anatomy of a modern scheduler

The key difference: cron is a monolith. You write both when and what to do in one crontab line.

systemd splits these into two separate unit files:

  1. .service (service unit): describes WHAT to do (e.g. run a script).
  2. .timer (timer unit): describes WHEN to do it (e.g. every day at 3:00 AM).

This split gives us incredible flexibility.

Step 1. Hands-on: preparing the task (the .service unit)

Let’s say we have a simple script for cleaning up a cache or temp files.

First, let’s create the script itself:

Bash

# Create a directory for our "architect-grade" scripts
sudo mkdir -p /opt/admin-future/scripts

# Create the script
sudo nano /opt/admin-future/scripts/cleanup.sh

Script contents (cleanup.sh):

Bash

#!/bin/bash
# A simple cleanup script
# We use 'logger' to send messages to the systemd journal
# Much cleaner than 'echo "something" >> /var/log/my.log'

logger -t "AdminFutureCleanup" "Starting cleanup of /tmp and /var/tmp..."
find /tmp -type f -mtime +7 -delete
find /var/tmp -type f -mtime +7 -delete
logger -t "AdminFutureCleanup" "Cleanup complete."

Make it executable:

Bash

sudo chmod +x /opt/admin-future/scripts/cleanup.sh

Now let’s “wrap” this script in a systemd.service. This tells the system how to run it.

Create the unit file:

Bash

# 'system' means this is a system unit, not a user one
sudo nano /etc/systemd/system/cleanup.service

File contents (cleanup.service):

Ini, TOML

#
# /etc/systemd/system/cleanup.service
# Describes "WHAT" we're doing.
#

[Unit]
# Human-readable description. Shows up in 'systemctl status'
Description=Weekly temp directory cleanup
# This matters! We're saying our service shouldn't start
# until the system has reached multi-user mode and the
# network is up (this particular script doesn't need the
# network, but it's a good habit for backup jobs).
After=network.target

[Service]
# Type=oneshot means this is a "one-off" task,
# not a long-running daemon (like nginx, say).
Type=oneshot

# Full path to our script
ExecStart=/opt/admin-future/scripts/cleanup.sh

# --- "ARCHITECT" LEVEL (security) ---
# Run the script as an unprivileged user.
# (Don't forget to create it: useradd -r -s /bin/false cleanup_user)
# User=cleanup_user
# Group=cleanup_user

# Forbid the script from writing to /etc, /usr, etc.
ProtectSystem=strict
# Give the script its own private, isolated /tmp
PrivateTmp=true

[Install]
# This section isn't used for timers, but is useful
# for services that need to start at boot.
WantedBy=multi-user.target

We just created a service. We can run it manually to test it: sudo systemctl start cleanup.service

And (here it is!) check the logs: sudo journalctl -u cleanup.service

You’ll see clean output, including our messages from logger! Already better than cron.

Step 2. Creating the schedule (the .timer unit)

Our .service is ready. Now let’s tell the system when to run it. We’ll create a .timer file with the same name.

Create the timer file:

Bash

sudo nano /etc/systemd/system/cleanup.timer

File contents (cleanup.timer):

Ini, TOML

#
# /etc/systemd/system/cleanup.timer
# Describes "WHEN" we run cleanup.service
#

[Unit]
Description=Run the weekly cleanup
# Tell this timer which service to trigger — the one
# with the same name (cleanup.service)
Unit=cleanup.service

[Timer]
# The most important part.
# 'OnCalendar' replaces cron's schedule syntax.
# Format: DayOfWeek Year-Month-Day Hour:Minute:Second
#
# 'weekly' is a shortcut (like @weekly in cron)
# It fires at 00:00 on Monday.
OnCalendar=weekly

# Here's what the equivalent of '0 2 * * *' (every day at 2 AM) looks like
# OnCalendar=*-*-* 02:00:00

# --- "ARCHITECT" LEVEL (reliability) ---
# If the server was off at the scheduled run time
# (say, 2 AM), this option tells systemd to
# run the missed job right after boot.
# Cron can't do this.
Persistent=true

[Install]
# We "attach" our timer to the system target 'timers.target'
# This is the equivalent of 'chkconfig on' or 'update-rc.d' for timers
WantedBy=timers.target

Step 3. Activation and management

We’ve created two files. Now let’s “activate” them.

  1. Reload the systemd configuration so it “sees” our new files: Bash sudo systemctl daemon-reload
  2. Start and “enable” the TIMER. Important: we start and enable the *.timer file, not the *.service! The timer itself triggers the service on schedule. Bash # Start the timer (so it begins "ticking") sudo systemctl start cleanup.timer # Enable the timer to start automatically at boot sudo systemctl enable cleanup.timer
  3. Check your new crontab: instead of crontab -l, we use list-timers: Bash systemctl list-timers You’ll see something like this: NEXT LEFT LAST PASSED UNIT ACTIVATES Mon 2025-11-03 00:00:00 CET 5 days left n/a n/a cleanup.timer cleanup.service This output is 1000% more informative than cron. You can see:
    • NEXT: when the next run is.
    • LEFT: how much time is left.
    • LAST: when the last run happened.
    • PASSED: how much time has passed since the last run.
    • UNIT: the name of our timer.
    • ACTIVATES: which .service it triggers.

Conclusion: you’re now an “Architect,” not a “Cron Admin”

Let’s recap what we got for the extra 5 minutes it took to create two files:

  1. Centralized management: every task is visible in systemctl list-timers.
  2. Real logging: journalctl -u cleanup.service shows complete, rotated run logs with exact timing and completion status (success/failure).
  3. Dependency management: we can guarantee the backup script won’t start before the network is up (After=network-online.target) or the drive is mounted (Requires=mnt-backup.mount).
  4. Reliability: the Persistent=true option runs missed jobs after downtime.
  5. Security: we can sandbox the task, restrict its permissions (ProtectSystem=), and run it as a dedicated user (User=), turning it into a real sandbox.

You didn’t just automate a task. You integrated it into the operating system. You built a reliable, observable, secure process. That’s what “architect of the future” thinking looks like.


Tags: #linux #systemd #automation #cron #sysadmin #tutorial #devops #bash



Tags: #linux #systemd #devops #automation #sysadmin


🚀 Want to become tomorrow’s architect instead of just an admin? This article is only the tip of the iceberg. On our Telegram channel “Admin Future” we publish systems-thinking guides, ready-to-use scripts, AI tips, and checklists every day that you won’t find on Google. We don’t just post commands — we’re building a knowledge library for people who want to grow from admin to architect. Subscribe and become the expert in the room: Admin Future

МШ

Maksim Shapovalov

Systems administrator. 17+ years working with infrastructure.

Discuss your project ↗

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top