at command lets you queue a one-off task to run once at a specific time, so you don’t have to write and then remember to delete a cron entry for something you only need to run once.You’ve probably used cron to schedule tasks and then realized the job only needs to run once. A common example is giving a vendor temporary SSH access to a production server for troubleshooting. You might open the firewall for their IP address and plan to remove the rule a few hours later.
The problem is that it’s easy to forget. Production issues happen, priorities change, and before you know it, that temporary firewall rule is still sitting there long after it should have been removed.
This is where the at command comes in, which lets you schedule a one-time task to run at a specific time in the future, making it perfect for temporary changes that need to be automatically reversed later. While most Linux administrators know about cron, many overlook at because cron gets most of the attention.
In this guide, we’ll explain what at is, how to schedule a one-off task, and how to manage queued at jobs in Linux.
What Is the at Command in Linux
The at command lets you schedule a command or script to run once at a specific time in the future. You tell it when to run, provide the command, and Linux takes care of the rest.
Behind the scenes, at places the job in a queue, and a background service called atd checks that queue and runs the job when the scheduled time arrives.
This is what makes at different from cron.
cron is designed for recurring tasks, such as running a backup every night or generating a report every Monday and at is designed for one-time tasks that only need to happen once.
For example, you might use at to:
- Restart a service later tonight during a maintenance window.
- Delete temporary files after a few hours.
- Re-enable a firewall rule after troubleshooting.
- Shut down a test server at the end of the day.
- Send yourself a reminder or notification at a specific time.
Think of it this way:
- If a task needs to run every day, use
cron. - If a task needs to run once at a specific time, use
at.
A good rule of thumb is that if you find yourself creating a cron job and then deleting it immediately after it runs, that task probably should have been scheduled with at in the first place.
Installing at and Starting the atd Service
On many Linux distributions, the at package is not installed by default, so you’ll need to install it first.
sudo apt install at [On Debian, Ubuntu and Mint] sudo dnf install at [On RHEL/CentOS/Fedora and Rocky/AlmaLinux] sudo apk add at [On Alpine Linux] sudo pacman -S at [On Arch Linux] sudo zypper install at [On OpenSUSE] sudo pkg install at [On FreeBSD]
This installs the at command along with related utilities such as atq and atrm. It also installs the atd service, which is responsible for running scheduled jobs at the correct time.
After installation, make sure the service is running and configured to start automatically after a reboot:
sudo systemctl enable --now atd sudo systemctl status atd
Output:
● atd.service - Deferred execution scheduler
Loaded: loaded (/usr/lib/systemd/system/atd.service; enabled; preset: enab>
Active: active (running) since Thu 2026-06-18 10:54:36 IST; 23s ago
Invocation: 9656ef36140d404eae309ec9642e402a
Docs: man:atd(8)
Main PID: 4804 (atd)
Tasks: 1 (limit: 8683)
Memory: 256K (peak: 2M)
CPU: 8ms
CGroup: /system.slice/atd.service
└─4804 /usr/sbin/atd -f
If you see that, the service is working correctly and ready to process scheduled jobs.
If atd is stopped or not running, any jobs you schedule with at will remain in the queue and never execute, which is one of the most common reasons new users think the at command isn’t working, so it’s always worth checking the service status first when troubleshooting.
atd running for the first time, who keeps forgetting to clean up old cron entries.Scheduling a One-Time Task with at
Let’s go back to the example of giving a vendor temporary SSH access to a server, where you already opened the firewall to their IP.
On Ubuntu/Debian (ufw):
sudo ufw allow from 203.0.113.50 to any port 22
On RHEL/Rocky Linux (firewalld):
sudo firewall-cmd --add-rich-rule='rule family="ipv4" source address="203.0.113.50" port port="22" protocol="tcp" accept'
Rather than relying on yourself to remember to remove that rule later, you can schedule the removal right away using at.
For example, to remove the firewall rule at 10:00 PM:
at 22:00
You’ll be dropped into an interactive prompt where you can enter the command you want to run:
On Ubuntu/Debian:
warning: commands will be executed using /bin/sh at Thu Jun 18 22:00:00 2026 at> ufw delete allow from 203.0.113.50 to any port 22 at> <EOT> job 3 at Thu Jun 18 22:00:00 2026
On RHEL/Rocky Linux:
warning: commands will be executed using /bin/sh at Thu Jun 18 22:00:00 2026 at> firewall-cmd --remove-rich-rule='rule family="ipv4" source address="203.0.113.50" port port="22" protocol="tcp" accept' at> <EOT> job 3 at Thu Jun 18 22:00:00 2026
After pressing Ctrl+D, at saves the job and displays a job number. In this example, job 3 has been scheduled to run at 10:00 PM.
One useful feature is that if the specified time has already passed today, at automatically schedules the job for the same time tomorrow.
Scheduling a Job in One Line
If you already know the command you want to run, you can skip the interactive prompt and send the command directly to at.
Ubuntu/Debian:
echo "ufw delete allow from 203.0.113.50 to any port 22" | at 22:00
RHEL/Rocky Linux:
echo "firewall-cmd --remove-rich-rule='rule family=\"ipv4\" source address=\"203.0.113.50\" port port=\"22\" protocol=\"tcp\" accept'" | at 22:00
The firewalld example contains escaped double quotes (\"). These backslashes prevent the shell from ending the quoted string too early and ensure the entire command is passed correctly to at.
Here’s what happens in the one-line version:
echo "..."prints the command as plain text.- The pipe
(|)sends that text to at. at 22:00reads the command and schedules it to run at 10:00 PM.
This approach is especially useful in scripts, automation tasks, or whenever you want to schedule a job quickly without entering the interactive prompt.
Schedule Linux Tasks Using Natural Time Expressions
One of the most useful features of the at command is that it understands human-friendly time expressions. Instead of calculating an exact time, you can simply tell Linux when you want the job to run relative to the current time.
For example, if a vendor only needs SSH access for the next two hours, you can schedule the firewall rule to be removed automatically after that period.
echo "ufw delete allow from 203.0.113.50 to any port 22" | at now + 2 hours
Output:
job 4 at Thu Jun 18 14:30:00 2026
The at command understands several natural time expressions:
at now + 30 minutes at now + 2 hours at now + 1 day at noon at midnight at noon tomorrow at 5pm tomorrow at 08:30 AM
View Scheduled Jobs Using atq
After scheduling one or more jobs with at, it’s a good idea to verify that they’re actually in the queue and waiting to run.
The atq command shows all pending jobs for the current user.
atq
Output:
3 Thu Jun 18 22:00:00 2026 a root 4 Thu Jun 18 14:30:00 2026 a root
Each line represents a scheduled job.
3 Thu Jun 18 22:00:00 2026 a root
Let’s break down the first entry:
3is the job ID.Thu Jun 18 22:00:00 2026is the scheduled execution time.ais the queue name (the default queue used by at).rootis the user who scheduled the job.
The atq command is useful whenever you want to confirm that a task has been scheduled correctly.
For example, after creating a job to remove a temporary firewall rule, you can run:
atq
and verify that the cleanup job is waiting in the queue before giving a vendor access to your server.
If atq returns no output, there are currently no pending jobs scheduled for your user account.
atq
at and the rest of the core command-line toolset, the 100+ Essential Linux Commands course on Pro TecMint covers it end to end.Removing a Job with atrm
If a job is sitting in the queue and you’ve changed your mind, maybe the vendor finished early and you want the firewall closed right now instead of waiting, atrm cancels it before it runs.
atrm job-number
Replace job-number with the number you saw in the atq output, that’s the angle bracket convention we’ll use anywhere in this guide for a value you need to fill in yourself.
atrm 4
There’s no output if it worked. Run atq again right after and that job number should be gone from the list. Just remember you still need to close the rule manually if you cancel the job, atrm only removes the scheduled task, not the firewall change itself.
atq just showed you a job you forgot you scheduled, else manages that server.Conclusion
The at command is one of those Linux tools that’s easy to overlook until you need it. Whenever you have a task that should run once at a specific time, it’s usually a better choice than creating a temporary cron job.
Have you been using cron for jobs that only needed to run once? Drop a comment with what you’re scheduling and I’ll tell you whether at is the better fit.






How to add date along with time.
Hi,
Very useful article
remove extra ‘d ‘ from atdd
# chkconfig –level 35 atdd on
@Jalal,
Thanks for finding it useful, and corrected in the writeup..
Very useful, thank you!
Good article, Gabriel!
Just my 5 cents to it.
“at” can be used to start something on background, like nohup staff, but more short:
$ echo “rsync remote_resource local_resource” | at now
or
$ at -f /path/to/my/script -m now
just notice “-f script” option – it’s very convenient to run more complex staff
“at” can be used instead of cron to run something repeatedly after the first execution ends. This can eliminate cronjobs, scheduled to run each minute with various locks to avoid to run several commands simultaneously. One can add at the end of its script:
—— bash code snippet —–
# script payload here
this_script_name=$(basename $0)
this_script_path=$(readlink –canonicalize $(dirname $0))
at -f $this_script_path/$this_script_name now
—— bash code snippet —–
this will start the script again when it’s finished.
Another interesting application that’s unable to implement with cron is periodically execution with random intervals. Mostly the same as abouve, but just add random delay:
—— bash code snippet —–
# script payload here
this_script_name=$(basename $0)
this_script_path=$(readlink –canonicalize $(dirname $0))
MIN_DELAY=15
RANDOM_BIAS=20
delay=$(( ($RANDOM % $RANDOM_BIAS) + $MIN_DELAY ))
at -f $this_script_path/$this_script_name now + $delay min
—— bash code snippet —–
That’s all :)