Skip to content

Cron Alternatives (Systemd Timers, Anacron, More)

DodaTech Updated 2026-06-28 7 min read

In this tutorial, you will learn about Cron Alternatives (Systemd Timers, Anacron, More). We cover key concepts, practical examples, and best practices to help you master this topic.

Explore cron alternatives: systemd timers for modern Linux, anacron for laptops, time-based job schedulers in Python, and when to choose each scheduling approach.

What You Learn

You will learn about systemd timers, anacron for non-24/7 systems, at for one-time scheduling, application-level schedulers like Celery Beat, and how to choose the right tool for different scheduling needs.

Why It Matters

Cron is not always the best choice. Systemd timers offer better logging, dependency management, and integration with modern Linux. Anacron handles laptops that sleep. Application schedulers provide database-backed schedules. Knowing alternatives prevents forcing the wrong tool.

Real-World Use

DodaTech uses systemd timers for system maintenance (log rotation, temp cleanup), Celery Beat for application tasks (report generation, notification dispatch), and anacron on developer laptops for local backups.

Systemd Timers

# Systemd timers consist of two files: .timer and .service

# /etc/systemd/system/daily-backup.service
# [Unit]
# Description=Daily database backup
#
# [Service]
# Type=oneshot
# ExecStart=/usr/local/bin/backup.sh
# User=backup
#
# [Install]
# WantedBy=multi-user.target

# /etc/systemd/system/daily-backup.timer
# [Unit]
# Description=Run daily backup at 3 AM
#
# [Timer]
# OnCalendar=daily
# OnCalendar=03:00:00
# Persistent=true
# RandomizedDelaySec=300
#
# [Install]
# WantedBy=timers.target

# Enable and start the timer
sudo systemctl daemon-reload
sudo systemctl enable daily-backup.timer
sudo systemctl start daily-backup.timer

# List timers
systemctl list-timers --all

# Check timer status
systemctl status daily-backup.timer

# View timer logs
journalctl -u daily-backup.service

# Manually trigger the service
sudo systemctl start daily-backup.service

Systemd Timer vs Cron Comparison

# Systemd timer advantages:
# 1. Logging: journalctl -u my-timer.service
# 2. Dependencies: After=network.target
# 3. Persistent=true catches missed runs after boot
# 4. RandomizedDelaySec prevents thundering herd
# 5. Resource control: CPUQuota, MemoryMax

# Cron advantages:
# 1. Simpler syntax: one line vs two files
# 2. More portable: works on any Unix
# 3. User crontabs without root
# 4. MAILTO for output delivery

# Systemd timer with complex schedule:
# /etc/systemd/system/custom-schedule.timer
# [Timer]
# OnCalendar=Mon..Fri 09:00:00
# OnCalendar=Sat 10:00:00
# Persistent=true

# Systemd calendar events:
# OnCalendar=hourly
# OnCalendar=daily
# OnCalendar=weekly
# OnCalendar=monthly
# OnCalendar=Mon..Fri 09:00:00
# OnCalendar=*-*-01 00:00:00 (first of month)
# OnCalendar=*:0/15 (every 15 minutes)

Anacron for Non-24/7 Systems

# Anacron runs jobs that were missed due to system downtime
# Ideal for laptops and desktops that are not always on

# /etc/anacrontab format:
# period delay job-identifier command
# 1    5    cron.daily      run-parts /etc/cron.daily
# 7    10   cron.weekly     run-parts /etc/cron.weekly
# 30   15   cron.monthly    run-parts /etc/cron.monthly

# Period: days between runs
# Delay: minutes to wait after boot before running
# Identifier: unique name (stored in /var/spool/anacron/)

# Add a custom anacron job:
# 7    20   my-weekly-job   /usr/local/bin/weekly-cleanup.sh

# Check when anacron last ran each job:
cat /var/spool/anacron/*

At Command for One-Time Scheduling

# at runs a command once at a specified time
# Unlike cron, no recurring schedules

# Schedule a one-time job
echo "/usr/local/bin/backup.sh" | at 03:00

# Schedule with date
echo "systemctl restart nginx" | at 23:00 today
echo "curl https://api.dodatech.com/deploy" | at 09:00 tomorrow

# Schedule with relative time
echo "shutdown -h now" | at now + 1 hour
echo "send_report.py" | at now + 30 minutes

# List pending at jobs
atq

# Remove a job
atrm 5    # Remove job number 5

# View at job details
at -c 5

Application-Level Scheduling

#!/usr/bin/env python3
"""Application-level scheduler using Python schedule library."""
import schedule
import time
import threading

class AppScheduler:
    def __init__(self):
        self.jobs = []

    def everyday_at(self, time_str, func):
        schedule.every().day.at(time_str).do(func)

    def every_n_minutes(self, minutes, func):
        schedule.every(minutes).minutes.do(func)

    def on_monday(self, time_str, func):
        schedule.every().monday.at(time_str).do(func)

    def on_weekdays(self, time_str, func):
        schedule.every().monday.at(time_str).do(func)
        schedule.every().tuesday.at(time_str).do(func)
        schedule.every().wednesday.at(time_str).do(func)
        schedule.every().thursday.at(time_str).do(func)
        schedule.every().friday.at(time_str).do(func)

    def run_threaded(self):
        def loop():
            while True:
                schedule.run_pending()
                time.sleep(30)
        thread = threading.Thread(target=loop, daemon=True)
        thread.start()

    def run_once(self):
        schedule.run_pending()

def generate_report():
    print("Weekly report generated")

def cache_warm():
    print("Cache warmed")

scheduler = AppScheduler()
scheduler.everyday_at("03:00", lambda: print("Backup running"))
scheduler.every_n_minutes(30, cache_warm)
scheduler.on_monday("09:00", generate_report)
scheduler.run_threaded()
time.sleep(65)

Celery Beat Scheduler

# celery_app.py
from celery import Celery
from celery.schedules import crontab

app = Celery('tasks', broker='redis://localhost:6379')

app.conf.beat_schedule = {
    'add-every-30-seconds': {
        'task': 'tasks.add',
        'schedule': 30.0,
        'args': (16, 16),
    },
    'daily-report': {
        'task': 'tasks.generate_report',
        'schedule': crontab(hour=9, minute=0),
    },
    'weekly-cleanup': {
        'task': 'tasks.cleanup',
        'schedule': crontab(hour=2, minute=0, day_of_week=0),
    },
    'monthly-billing': {
        'task': 'tasks.run_billing',
        'schedule': crontab(0, 0, day_of_month='1'),
    },
}

@app.task
def add(x, y):
    return x + y

@app.task
def generate_report():
    return {'report': 'generated'}

@app.task
def cleanup():
    return {'cleaned': True}

@app.task
def run_billing():
    return {'billing': 'completed'}

Choosing the Right Scheduler

schedulers = {
    'cron': {
        'best_for': 'Simple recurring tasks on always-on servers',
        'pros': 'Universal, simple syntax, no dependencies',
        'cons': 'No dependency management, minimal logging',
    },
    'systemd_timer': {
        'best_for': 'System-level tasks on modern Linux',
        'pros': 'Journal logging, dependencies, resource control',
        'cons': 'Linux only, two-file setup',
    },
    'anacron': {
        'best_for': 'Laptops and desktops that sleep',
        'pros': 'Runs missed jobs after downtime',
        'cons': 'Daily granularity only',
    },
    'celery_beat': {
        'best_for': 'Application tasks with database integration',
        'pros': 'Dynamic schedules, distributed execution',
        'cons': 'Requires Redis/RabbitMQ, complex setup',
    },
    'kubernetes_cronjob': {
        'best_for': 'Containerized workloads in Kubernetes',
        'pros': 'Declarative, self-healing, scalable',
        'cons': 'Requires Kubernetes cluster',
    },
}

for name, info in schedulers.items():
    print(f"{name:20s}")
    print(f"  Best for: {info['best_for']}")
    print(f"  Pros: {info['pros']}")
    print(f"  Cons: {info['cons']}")
    print()

Common Mistakes

1. Using Cron for One-Time Jobs

Cron only does recurring schedules. Use at for one-time jobs. Do not create a cron job and then delete it.

2. Ignoring Systemd Timers on Modern Systems

Systemd timers provide better integration than cron for system-level tasks. Journalctl gives you structured logging automatically.

3. Using Cron on Laptops

Laptops sleep and may miss Cron Jobs. Use anacron or systemd timers with Persistent=true for non-24/7 devices.

4. Application Scheduler Without Persistence

In-memory schedulers lose schedules on restart. Use database-backed schedulers like Celery Beat for production.

5. Mixing System and Application Schedules

System tasks (log rotation, temp files) belong in system schedulers (cron, systemd). Application tasks belong in application schedulers (Celery Beat).

Practice Questions

1. What is the main advantage of systemd timers over cron?

Integrated logging with journalctl, dependency management (After=network.target), resource control (CPUQuota, MemoryMax), and persistent missed-run detection.

2. When should you use anacron instead of cron?

On systems that are not always running, like laptops. Anacron runs missed jobs after the system powers on.

3. What is Celery Beat?

A database-backed scheduler for Celery that supports dynamic schedule changes at runtime without restarting workers.

4. How do you schedule a one-time job in Linux?

Use the at command: echo "command" | at 03:00. The job runs once at the specified time and is removed.

Challenge

Design a scheduling Strategy for a web application on a laptop used for development: system maintenance logs (use systemd timers), daily database backup (anacron with 1-hour delay after boot), cache warming every 30 minutes (application scheduler with persistence), deployment tasks (at command for one-time use).

FAQ

What is the main advantage of systemd timers over cron?

Integrated logging via journalctl, dependency management (network.target, etc.), resource control (CPU/memory limits), and Persistent=true for catching missed runs after downtime.

Can systemd timers replace cron entirely?

On modern Linux systems, yes. Systemd timers can do everything cron does with better integration. However, cron is still simpler for quick setups.

What happens if a systemd timer misses a run?

If Persistent=true is set, the timer runs immediately when the system starts. If Persistent=false, missed runs are skipped.

Is anacron still needed with systemd timers?

Systemd timers with Persistent=true handle missed runs similarly to anacron. But anacron is simpler for non-systemd systems and legacy setups.

When should I use an application scheduler instead of system cron?

When schedules need to change at runtime, when they depend on application state, or when they need access to application libraries and database connections.

Mini Project: Systemd Timer Setup

# Create a systemd service and timer for log cleanup

# /etc/systemd/system/log-cleanup.service
cat > /etc/systemd/system/log-cleanup.service << 'EOF'
[Unit]
Description=Clean old log files
After=local-fs.target

[Service]
Type=oneshot
ExecStart=/usr/bin/find /var/log -name "*.log" -mtime +30 -delete
ExecStart=/usr/bin/journalctl --vacuum-time=30d
User=root
StandardOutput=journal
EOF

# /etc/systemd/system/log-cleanup.timer
cat > /etc/systemd/system/log-cleanup.timer << 'EOF'
[Unit]
Description=Run log cleanup daily
Requires=log-cleanup.service

[Timer]
OnCalendar=daily
Persistent=true
RandomizedDelaySec=600

[Install]
WantedBy=timers.target
EOF

# Enable and start
sudo systemctl daemon-reload
sudo systemctl enable log-cleanup.timer
sudo systemctl start log-cleanup.timer

# Verify
systemctl list-timers log-cleanup.timer
journalctl -u log-cleanup.service

What's Next

Now that you understand cron alternatives, explore cron monitoring best practices, then learn about cron health checks for ensuring jobs run successfully.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro