Updated: 12 October 2026 · Applies to: at 3.2.5 on Debian 13, Ubuntu 26.04 and AlmaLinux 10.2 (tested); the same package and commands on Rocky Linux and RHEL
The at command in Linux runs a command once, at a time you choose. Pipe the command into at with the time: echo "/root/backup.sh" | at 23:30, or echo "reboot" | at now + 2 hours. atq lists the waiting jobs and atrm NUMBER cancels one. The atd service must be running. For a job that repeats every day or every hour, use cron instead.
Quick reference
- Run a command at a time:
echo "COMMAND" | at 23:55 - Run a script file:
at -f /root/job.sh 03:00 - Run it in 30 minutes:
echo "COMMAND" | at now + 30 minutes - List pending jobs:
atq(same asat -l) - Show what a job will run:
at -c 4 - Cancel a job:
atrm 4(same asat -r 4orat -d 4) - Run when the server is not busy:
echo "COMMAND" | batch
1. Install at and start atd
Minimal server images often do not include at. Install it and start the atd service, which runs the jobs:
# Debian and Ubuntu sudo apt install at # AlmaLinux, Rocky Linux and RHEL sudo dnf install at sudo systemctl enable --now atd systemctl status atd
The unit is called atd.service on all of these systems. If atd is not running, at still accepts the job but warns you:
$ echo 'date' | at now + 1 minute warning: commands will be executed using /bin/sh job 1 at Sun Oct 11 14:51:00 2026 Can't open /run/atd.pid to signal atd. No atd running?
In our test the job stayed in the queue and ran once atd was started.
2. Schedule a job
There are three ways to give at the commands:
- Type them. Run
atwith a time, type one command per line at theat>prompt, then press Ctrl+D on an empty line:$ at now + 5 minutes warning: commands will be executed using /bin/sh at Sun Oct 11 15:26:00 2026 at> echo hello > /tmp/hi.txt at> <EOT> job 1 at Sun Oct 11 15:26:00 2026
- Pipe one command in. This is the quickest way and works in scripts:
echo "/usr/local/bin/report.sh > /root/report.log 2>&1" | at 23:55
- Read a script file with
-f:$ at -f /root/job.sh now + 1 minute warning: commands will be executed using /bin/sh job 12 at Sun Oct 11 14:51:00 2026
The warning is normal: the job runs with /bin/sh, so avoid Bash-only syntax in the commands, or call a script that starts with #!/bin/bash. According to the at manual, the job keeps the working directory, the umask and most environment variables you had when you scheduled it. at -v prints the run time before it reads the commands, which is a quick way to check how a time was understood.
3. Time formats that at understands
We submitted each of these on Debian 13 at 14:50 UTC on a Sunday; the result on the right is the time at reported. AlmaLinux 10 gave the same results.
now + 1 minute: 14:51 the same day. Units are minutes, hours, days and weeks.now + 2 hours: 16:50 the same day.23:55: today at 23:55, or tomorrow if that time has passed.noon tomorrow: Monday 12:00.midnight(00:00) andteatime(16:00) also work.9am next monday: Monday 09:00.midnight + 3 days: Wednesday 00:00. It counts from the midnight that started today, not the next one.10:30 AM Oct 20: 20 October at 10:30.09:00 2026-10-20: 20 October at 09:00. The date must come after the time.-t 202610201030: 20 October at 10:30, in the format [[CC]YY]MMDDhhmm.
A time at cannot read is rejected and nothing is queued:
$ echo 'echo x' | at 25:00 Problem in hours specification. Last token seen: 25:00 Garbled time
Times are in the server's own time zone, not yours. Check it with date before you schedule anything important; our lab containers ran in UTC. See how to check the time zone in Linux if you need to check or change it.
4. List, inspect and cancel jobs
$ atq 4 Mon Oct 12 12:00:00 2026 a root 5 Sun Oct 11 23:55:00 2026 a root 15 Sun Oct 11 14:50:00 2026 b root
Each line shows the job number, the run time, the queue and the owner (output shortened). Queue a is for at jobs and b for batch. The list is not in time order, so read the dates. Root sees every user's jobs; other users see only their own.
at -c 4prints the whole job: the saved environment first, and your commands at the end. Use it to check what will really run.atrm 4cancels job 4. It prints nothing when it works.- To change a job, cancel it and schedule it again;
athas no edit option.
5. Where the output goes
At does not print to your terminal. According to its manual, anything the job writes to standard output or standard error is mailed to you with /usr/sbin/sendmail. -m sends a mail even when there is no output, and -M never sends one. Most VPS images have no mail server, so redirect the output to a file:
echo "/root/backup.sh >> /root/backup.log 2>&1" | at 02:00
6. batch: run a job when the server is quiet
batch takes commands the same way as at but has no time: it starts the job as soon as the load average drops below a limit. According to the atd manual pages we read, the limit is 1.5 on Debian and Ubuntu and 0.8 on AlmaLinux. Start atd with -l to change it (on AlmaLinux, through OPTS in /etc/sysconfig/atd).
echo "tar -czf /root/site.tar.gz /var/www" | batch
Lab example: on our busy single-CPU test machine (load average about 1.5 to 1.9), a batch job waited about three minutes before it ran. That is the expected behaviour, not a fault.
7. Allow or block users
Root can always use at. For other users, according to the at.allow manual: if /etc/at.allow exists, only the users listed in it may use at; otherwise everyone not listed in /etc/at.deny may. Debian and Ubuntu ship an at.deny that lists system accounts; on AlmaLinux 10 it is empty, so every user may use at. A blocked user sees:
$ echo id | at now + 5 minutes You do not have permission to use at.
at, cron or a systemd timer?
- Once, at a set time:
at. - Repeating (every night, every 5 minutes): cron. See how to add cron jobs and how to edit cron jobs. To list every scheduled job on a server, including
atq, see list all cron jobs for all users. - Without at installed: on systemd servers,
sudo systemd-run --on-active=3600 /usr/local/bin/report.shruns the command once, an hour later, as a temporary timer (according to the systemd-run manual).systemctl list-timersshows it. - For jobs that must run even if the server was off at the planned time, see scheduling jobs with anacron.
On shared hosting without root access, use your control panel's cron jobs page instead (cron jobs in cPanel). On a Ucartz KVM VPS you have full root access, so you can install and use at as shown above.
Official documentation: at manual page.