at schedules a one-time job to run at a specific future time — perfect for “run this once tonight” without editing a crontab. anacron solves a different problem — it guarantees that periodic jobs run even if the system was powered off when they were scheduled. Where cron silently misses jobs on a shutdown system, anacron catches up the moment the system comes back up.
Both tools fill gaps that cron and systemd timers don’t cover as elegantly. Every sysadmin’s toolkit should include all four.
Use when: You need to run something once at a specific future time.
Key point: Job runs once and is gone.
Use when: Periodic jobs must not be missed on systems that are not always on.
Key point: No exact time — runs N days after last run.
# Install at if not present sudo dnf install -y at # RHEL/Rocky sudo apt install -y at # Debian/Ubuntu sudo systemctl enable --now atd # Schedule a command at a specific time echo "/usr/local/bin/maintenance.sh" | at 02:00 echo "systemctl restart nginx" | at 2:30 AM echo "/usr/bin/backup.sh" | at midnight echo "/usr/bin/backup.sh" | at noon echo "/usr/bin/report.sh" | at 9am tomorrow echo "/usr/bin/cleanup.sh" | at now + 2 hours echo "/usr/bin/deploy.sh" | at now + 30 minutes echo "/usr/bin/task.sh" | at 3pm next friday echo "/usr/bin/task.sh" | at 10:00 AM Jul 4Output after scheduling:
at 2am
with no command to get a prompt where you type multiple commands.
Press Ctrl-D when done.
# List pending at jobs atq at -l # same as atqatq output:
# View the contents of a job at -c 12 # Remove a pending job atrm 12 atrm 12 13 14 # remove multiple # Schedule from a file at -f /usr/local/bin/maintenance.sh 2:00 AM tomorrow # batch -- runs when system load drops below 1.5 echo "/usr/local/bin/heavy-report.sh" | batch
at
saves your current PATH and environment variables when you schedule the job
and replays them at run time. This is why at jobs are less prone to
PATH-not-found failures than cron jobs.
# Restart a service after a config change in 5 minutes echo "systemctl restart nginx" | at now + 5 minutes # One-time database backup before a risky operation echo "pg_dump mydb > /backup/pre-migration-$(date +%Y%m%d).sql" | at now + 1 minute # Broadcast a reminder to all logged-in users echo "echo 'Maintenance starting in 10 minutes!' | wall" | at 9:50 PM # Access control cat /etc/at.allow # whitelist -- only these users can use at cat /etc/at.deny # blacklist -- everyone except listed users # Check atd service sudo systemctl status atd
batch queues a one-time job
just like at, but holds it until the system load average
drops below 1.5. Perfect for CPU-intensive jobs that should not
impact normal operation — let the system decide when it has
headroom to run them.
Anacron maintains a timestamp for each job. When it runs it checks how many days since each job last executed. If the interval has passed it runs the job regardless of whether the system was on when it was due:
# Install anacron sudo dnf install -y cronie-anacron # RHEL/Rocky sudo apt install -y anacron # Debian/Ubuntu # View the anacron configuration cat /etc/anacrontab/etc/anacrontab format:
# View timestamp files -- one per job-id
ls -la /var/spool/anacron/
cat /var/spool/anacron/cron.daily
Timestamp file -- just a date:
# Force all jobs to run now (ignore timestamps) sudo anacron -f # Test mode -- show what would run, don't actually run sudo anacron -n -T # Run with debug output sudo anacron -d # Reset a job's timestamp to force it to run next time echo 19700101 | sudo tee /var/spool/anacron/cron.weekly
19700101 (epoch) guarantees
the interval has passed — anacron will run that job on its
next wakeup. Useful for testing or recovering from a skipped job.
# Add custom jobs to /etc/anacrontab sudo tee -a /etc/anacrontab << 'EOF' # Run weekly DB backup -- every 7 days, 20 min delay 7 20 db-weekly-backup /usr/local/bin/db-backup.sh # Monthly log archive -- every 30 days, 25 min delay 30 25 log-monthly-archive /usr/local/bin/archive-logs.sh EOF # Verify syntax sudo anacron -T && echo "Syntax OK"
Persistent=true.
On RHEL/Rocky, anacron is triggered automatically via cron:
# On RHEL, /etc/cron.hourly/0anacron triggers anacron
cat /etc/cron.hourly/0anacron
0anacron script:
/etc/cron.d/anacron or the systemd
anacron.timer. The behavior is the same regardless
of how it is triggered.
| Need | Best Tool | Why |
|---|---|---|
| Run once at 2 AM tonight | at | One-time future execution |
| Run every weekday at 8 AM | cron | Recurring fixed-time schedule |
| Run daily but system may be off | anacron or systemd timer (Persistent=true) | Catch-up on missed runs |
| Run 5 minutes after every boot | systemd timer (OnBootSec=5min) | Boot-relative monotonic timer |
| Run every 15 minutes | cron (*/15) or systemd timer (*:0/15) | Both work equally well |
| Run when system load is low | batch | Load-based one-time execution |
| System-level with full logging | systemd timer | journald integration, dependencies |
| Simple one-liner job | cron | Lowest setup overhead |
| Command | What it does |
|---|---|
| echo "cmd" | at TIME | Schedule one-time job |
| at -f script.sh TIME | Schedule script file |
| atq | List pending jobs |
| at -c JOBNUM | View job contents |
| atrm JOBNUM | Remove a pending job |
| echo "cmd" | batch | Run when load is low |
| systemctl status atd | Check at daemon |
| Expression | Meaning |
|---|---|
| now + 30 minutes | 30 minutes from now |
| now + 2 hours | 2 hours from now |
| 2:30 AM | Next 2:30 AM |
| midnight | Tonight at midnight |
| noon | Today at noon |
| 9am tomorrow | Tomorrow morning |
| 3pm next friday | Next Friday afternoon |
| 10:00 AM Jul 4 | Specific date and time |
| Command | What it does |
|---|---|
| cat /etc/anacrontab | View anacron job configuration |
| ls /var/spool/anacron/ | View last-run timestamps |
| sudo anacron -f | Force run all jobs now |
| sudo anacron -T | Test/validate anacrontab syntax |
| sudo anacron -d | Debug mode -- show what would run |
| echo 19700101 | sudo tee /var/spool/anacron/JOBID | Reset timestamp to force next run |