Skip to content

Intermediate · Automation basics: scheduling, archives and shell scripts · topic 35 of 57 · 30 min

One-time jobs with at, and systemd timers

Explanation

at runs a command once at a set time (at 23:00, at now + 2 hours); atq lists waiting jobs and atrm removes them. systemd timers are the modern way to schedule: a .timer unit starts a .service unit on a calendar (OnCalendar=) or after a delay, logs to the journal, and can catch up on missed runs with Persistent=true.

at: run something once, later

Sometimes you need a job to run once: restart a service at midnight after a change, or remove a temporary firewall rule in two hours. at does this. It needs the atd service running (systemctl enable --now atd).

bash
$ at now + 2 hours
at> firewall-cmd --remove-port=9000/tcp
at> <Ctrl+D>
job 4 at Mon Oct 19 17:30:00 2026
$ echo "systemctl restart httpd" | at 23:00
$ at 02:00 tomorrow < /root/maintenance.sh
$ atq
4	Mon Oct 19 17:30:00 2026 a root
5	Mon Oct 19 23:00:00 2026 a root
$ at -c 5 | tail -2          # show what job 5 will run
$ atrm 5                     # cancel job 5
Press Ctrl+D on an empty line to finish typing the job.

batch is a relative of at: it runs a job when the system load is low. Access to at is controlled by /etc/at.allow and /etc/at.deny, like cron.

systemd timers

A timer unit is a schedule for a service unit. Two small files: backup.service says WHAT to run, and backup.timer says WHEN. Compared with cron, timers log every run in the journal (journalctl -u backup), can run missed jobs after the machine was off (Persistent=true), can add a random delay so 500 servers do not all hit the backup server at once, and use all of systemd's controls (user, resource limits, dependencies).

ini
# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup of /etc and /var/www

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
Type=oneshot: run the command to the end, then the service is done.
ini
# /etc/systemd/system/backup.timer
[Unit]
Description=Run backup.service every night at 01:30

[Timer]
OnCalendar=*-*-* 01:30:00
Persistent=true
RandomizedDelaySec=10min

[Install]
WantedBy=timers.target
The timer starts the service with the same name (backup.service) by default.
bash
# systemctl daemon-reload
# systemctl enable --now backup.timer      # enable the TIMER, not the service
Created symlink /etc/systemd/system/timers.target.wants/backup.timer → /etc/systemd/system/backup.timer.
# systemctl list-timers backup.timer
NEXT                        LEFT     LAST PASSED UNIT         ACTIVATES
Tue 2026-10-20 01:34:12 IST 10h left n/a  n/a    backup.timer backup.service
$ systemd-analyze calendar "Mon..Fri *-*-* 09:00:00"
  Original form: Mon..Fri *-*-* 09:00:00
Normalized form: Mon..Fri *-*-* 09:00:00
    Next elapse: Tue 2026-10-20 09:00:00 IST
systemd-analyze calendar checks an OnCalendar expression and shows the next run.
OnCalendar valueMeaning
dailyEvery day at 00:00
hourlyEvery hour at minute 0
weeklyMondays at 00:00
*-*-* 01:30:00Every day at 01:30
Mon..Fri 09:00Weekdays at 09:00
*-*-01 00:00:00The 1st of every month

Timers can also be relative: OnBootSec=5min runs 5 minutes after boot, OnUnitActiveSec=1h runs again 1 hour after the last run. RHEL itself uses timers, for example dnf-makecache.timer and fstrim.timer; systemctl list-timers --all shows them.

Common mistakes

  • Enabling the .service instead of the .timer. The service then runs once at boot, not on schedule.
  • Forgetting systemctl daemon-reload after creating the files.
  • Using at without atd running: jobs are queued but never run.
  • A typo in OnCalendar. Always test it with systemd-analyze calendar.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

One-time jobs with at, and systemd timers · 1/6

echo "systemctl restart httpd" | at 23:00
atq
atrm 5

at runs a job once, at a time you choose. It needs the atd service.

Prefer a video by a teacher? Find free videos on “systemd timers tutorial oncalendar persistent at command linux” on YouTube ↗

In real life: Catching up after a power cut in a small-town office

A cooperative society office in a small town in Maharashtra has frequent power cuts at night, so its cron backup at 01:00 was often missed. The admin moved it to a systemd timer with Persistent=true. Now, if the server was off at 01:00, the backup runs as soon as it boots, and journalctl -u backup shows every run and its result.

bash
# journalctl -u backup.service --since yesterday --no-pager | tail -2
Oct 19 06:12:40 coop01 systemd[1]: Starting Nightly backup of /etc and /var/www...
Oct 19 06:13:55 coop01 systemd[1]: backup.service: Deactivated successfully.

Practice (0/3 done)

Task 1 · Question

Which command lists jobs waiting to run with at?

Task 2 · Question

You created report.service and report.timer. Which command schedules it?

Task 3 · Practice

Create a timer that writes the date and free disk space of / to /var/log/diskcheck.log every 10 minutes, and prove it ran twice.

Quick quiz

Question 1

1.Which service must run for at jobs to execute?

Question 2

2.What does Persistent=true do in a timer?

Question 3

3.Which tool checks an OnCalendar expression and shows the next run?

Question 4

4.What does OnCalendar=weekly mean?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

Which service must run for at jobs to execute?

Answer: atd

atd runs at jobs.

What does Persistent=true do in a timer?

Answer: Runs a missed job soon after boot if the machine was off at the scheduled time

It catches up on missed runs.

Which tool checks an OnCalendar expression and shows the next run?

Answer: systemd-analyze calendar

systemd-analyze calendar.

What does OnCalendar=weekly mean?

Answer: Mondays at 00:00

weekly is shorthand for Mon *-*-* 00:00:00.

Without /var/log/journal, where does journald keep logs?Related

Answer: /run/log/journal, lost at reboot

With Storage=auto and no /var/log/journal, logs stay in /run (RAM).

From Logs: journalctl, rsyslog and a persistent journal

A service shows "active" and "disabled". What happens after a reboot?Related

Answer: It does not start

Disabled means it is not started at boot.

From systemd services and systemctl

Why does systemctl --user fail after su - webops?Related

Answer: su does not start a full login session with the user's systemd and runtime directory

Log in directly (SSH) as the user.

From Persistent storage and containers as systemd services

You started a service with systemctl start, but after a reboot it is not running. What did you forget?Related

Answer: systemctl enable

start runs it now; enable makes it start at boot.

From Processes, signals and systemd services

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 3 more topics in “Automation basics: scheduling, archives and shell scripts” before its exam: Archives and compression: tar, gzip, bzip2 and xz, Bash scripts: variables, quoting, arguments and exit codes, Bash scripts: conditions, loops and safe habits.
  2. Module 7 exam: Automation basics · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Boot, recovery and containers. 57 topics left in the course before the final exam.

← Previous: Scheduling with cron and crontab