You do not always need cron or a systemd timer. When an action must run only once, in 10 minutes, tonight at 11 pm, or tomorrow morning, at is the most direct tool. This guide shows the at + atq + atrm trio, with outputs measured on Debian 13 and AlmaLinux 10, and the traps that make you believe a job is scheduled when it will never run.
What you will learn
- Tell apart the uses of
atandcron - Schedule a one-off command with
at - List the queue with
atqand read a job back withat -c - Delete a scheduled job with
atrm - Check that a job did run
- Avoid the classic mistakes (service, rights, environment)
In which context?
at is useful when an action must fire later, but only once. The atd daemon keeps the job in a queue, runs it when due, then forgets it:
- restart a service outside production hours;
- run a cleanup script in 15 minutes;
- start an integrity check tonight;
- schedule a technical reminder (mail, report, rotation).
at vs cron in 30 seconds
The difference fits in one word: at runs once, cron repeats. An at job leaves the queue as soon as it has run, a crontab line stays in place and fires again at each due time.
- Use
atfor a one-off run. - Use
cronfor a recurring run.
A simple mental model:
- "Run this script tomorrow at 09:00" ->
at - "Run this script every day at 09:00" ->
cron
Prerequisites
Do not assume at is there: on a minimal AlmaLinux 10 or RHEL 10,
neither the package nor the unit exists. Start by checking the state of
the atd service:
# Check that the atd service is activesystemctl status atd --no-pagerYou should see Active: active (running).
Counter-intuitive, and a classic false positive: with atd stopped, submitting
a job still succeeds. at accepts the work, queues it, and merely
prints a warning, measured identically on Debian 13 and AlmaLinux 10:
warning: commands will be executed using /bin/shjob 1 at Thu Jul 23 07:36:00 2026Can't open /run/atd.pid to signal atd. No atd running?atq then lists a job that will never run. So never conclude
that the service is active because a job sits in the queue: check
systemctl is-active atd separately.
Step 1: schedule a one-off job with at
Here we schedule a write into an output file. The -f option reads the
commands from a file rather than from standard input, and the expression
now + 2 minutes sets a relative due time.
mkdir -p /tmp/at-labprintf '%s\n' 'echo "AT_OK" >> /tmp/at-lab/output.txt' > /tmp/at-lab/job.shat -f /tmp/at-lab/job.sh now + 2 minutesExpected output (example):
warning: commands will be executed using /bin/shjob 9 at Mon Apr 20 21:24:00 2026The warning says /bin/sh, and it is only half true. On Debian
and Ubuntu, the job is indeed run by /bin/sh, hence by dash. On
AlmaLinux 10, at -c shows that the job body is handed to
${SHELL:-/bin/sh}, that is to the shell of the user who submitted the
job, often bash. In both cases, the shell is not interactive: your
aliases and functions do not exist. So write portable commands,
with absolute paths.
The time is best written explicitly too. Three behaviours measured on Debian 13 and AlmaLinux 10 avoid bad surprises:
at 9:00submitted at 20:49 is scheduled the next day at 9:00, without a warning;at 0815is read as 08:15, not as August 15; writeat 09:00 Aug 15for a date;at now - 1 houris refused withat: refusing to create job destined in the past.
Step 2: inspect the queue with atq
atq lists the pending jobs of the current user. It is the check to
run right after scheduling, to verify the computed time of
at.
atqExpected output (example):
9 Mon Apr 20 21:24:00 2026 a bob- first column: job identifier;
- date/time: planned run;
- letter
a: the queue of the job,aby default; - last field: owning user.
To read back what a job will actually run, at -c followed by the
identifier prints the full script recorded by at. It starts with the
environment captured at submission time, then ends with your
commands:
at -c 9 | tail -5Step 3: cancel a job with atrm
atrm removes a job from the queue before it runs, naming it by the
identifier read in the first column of atq. Run atq again afterwards
to confirm the deletion.
# Replace 9 with the actual identifieratrm 9atqIf the queue is empty, atq returns nothing.
Step 4: check the run when you do not delete the job
A job that has run leaves the queue: atq keeps no history. The only
proof of the run is therefore the effect produced by the script, here a line
in the output file. Schedule a job and let it run:
at -f /tmp/at-lab/job.sh now + 1 minuteAfter ~1 minute:
cat /tmp/at-lab/output.txtYou should see at least one AT_OK line.
Control who may use at
Two files govern access, on the same principle as for cron:
/etc/at.allow, when it exists, is an allow list and makes
/etc/at.deny irrelevant; otherwise, the accounts listed in /etc/at.deny
are refused. A blocked account gets You do not have permission to use at.
The shipped values differ. On Debian 13 and Ubuntu 24.04,
/etc/at.deny already lists the system accounts (alias, backup, bin,
daemon, ftp...). On AlmaLinux 10, it is empty. In both cases,
creating an empty /etc/at.allow forbids at to every account except root
(measured on Debian 13).
Best practices
An at job runs with nobody in front of the screen, in a reduced
environment: these rules aim at making it fail visibly rather than
silently.
- Use absolute paths in the scripts called by
at. - Prefer a script (
at -f script.sh) to a complex inline command. - Log the output (
>> /var/log/... 2>&1) to ease the diagnosis. - Test the command directly before scheduling it.
Quick troubleshooting
Read this table by symptom: the first three lines cover the
most frequent case, an atd service missing or stopped, which lets the queue
fill up while nothing runs.
| Symptom | Likely cause | Fix |
|---|---|---|
at: command not found | package not installed | sudo dnf install at or sudo apt install at |
systemctl is-enabled atd answers not-found | the unit does not exist: the package is not installed | install the package, then enable --now |
The service is enabled but inactive (dead) | the installation enables without starting | sudo systemctl start atd |
| A time already past is accepted without a warning | at 9:00 submitted in the afternoon is moved to the next day | Check with atq; an explicitly past instant (now - 1 hour) is refused |
0815 read as 08:15, not August 15 | a time form, not a date | Write the date explicitly (at 09:00 Aug 15) |
| The job output disappears | no mail agent installed (Exec failed for mail command) | Redirect explicitly to a file in the scheduled command |
| job never run | atd inactive | sudo systemctl enable --now atd |
| job present but no effect | invalid script or incomplete PATH | use absolute paths, read the job back with at -c |
You do not have permission to use at. | at.allow/at.deny policy | check /etc/at.allow and /etc/at.deny |
Put it into practice
Schedule a one-off job with at on a VM, then check that it runs at the planned time and that the queue is handled correctly.
Key points
atschedules a one-off job,atqlists the pending jobs,atrmdeletes one.at -c <id>prints the script actually recorded, environment included.- The
atdservice must be active: a job in the queue proves nothing. - The job runs in a non-interactive shell (
/bin/shon Debian, the user's$SHELLon RHEL): no aliases, absolute paths. /etc/at.allow, when it exists, takes precedence over/etc/at.deny.- In an admin/certification context,
atcomplementscronand timers.
Knowledge check
Check that the essentials of this guide stuck. The questions cover only what has just been explained here.
Knowledge check
Check what you have learned with this interactive quiz
How it works
- The timer starts when you click Start
- Multiple choice, true/false and short answer questions
- You can move between questions
- Detailed results are shown at the end
Starts the quiz and the timer
Review
(0/0)Score by domain
A domain groups several guides and gets several questions, so its score is more reliable than a per-topic score, which often rests on a single question.
Summary
Strengths
No topic passed at 80% on at least two questions yet.
Weak areas
No topic below the pass mark.
Recommended next
Hints before you try again?
Continue the course: Schedule tasks with systemd timers on Linux
A new full quiz with random questions
Work again only on the questions you missed