Your at job was queued for 3am and never ran, and now you need to know whether atd is even alive. This guide checks the daemon, shows how at and batch reach the spool, and makes one controlled change to batch scheduling. Allow about 15 minutes.
at package and systemd.at 3.2.5-2.1ubuntu3, the package installed here.Run these as your ordinary user. They change nothing and show which binary and package you are inspecting:
$ command -v atd
/usr/sbin/atd
$ dpkg-query -W -f='${Package} ${Version}\n' at
at 3.2.5-2.1ubuntu3
$ systemctl is-active atd.service
active
The local manual describes atd as the daemon that runs jobs queued by at. The installed systemd unit starts /usr/sbin/atd -f, so the service manager keeps the daemon in the foreground. That is normal.
Warning: Do not start a second copy by hand while the service is running.
Inspect the unit and recent messages before changing anything:
$ systemctl status --no-pager atd.service
$ journalctl -u atd.service -n 30 --no-pager
A healthy service normally shows active (running). The daemon does not print a regular progress log, and under the normal service unit errors go through syslog or the journal, not your terminal.
If the service is missing or stopped, the ordinary administrator action is:
$ sudo systemctl enable --now atd.service
$ systemctl is-enabled atd.service
enabled
$ systemctl is-active atd.service
active
Warning: That is an elevated, persistent change: it enables startup at boot and starts the scheduler now. If you enabled it by mistake, the direct undo is sudo systemctl disable --now atd.service. That stops all future processing by this daemon, so inspect pending work with atq and get the service owner's approval first.
atd is not the interface for adding or removing jobs. Queue a harmless future command with at, then list it:
$ printf '%s\n' 'printf "atd check\\n" > /tmp/atd-check.txt' | at now + 10 minutes
job 17 at Tue Sep 22 14:10:00 2026
$ atq
17 Tue Sep 22 14:10:00 2026 a YOUR_USER
Your job number and time will differ. The default at queue is a, and batch uses queue b. A job submitted to an uppercase queue is treated as a batch job.
If you only wanted to inspect submission, remove the test job before it runs:
$ atrm 17
$ atq
$ test ! -e /tmp/atd-check.txt && echo 'test job removed'
test job removed
Replace 17 with the number printed by your own submission. atrm removes a queued job; it cannot undo a command that has already started.
Warning: Never use a real destructive command as a test payload. Jobs run through /bin/sh, retain much of the submitting environment and umask, and can mail standard output and standard error through sendmail.
batch queues work for when the load average is below the daemon's limit. In this package the default limit is 1.5. Two options tune it:
-l sets the load limit. It decides whether batch work may start, based on load.-b sets the gap between batch starts. It is the minimum interval between the starts of two batch jobs, 60 seconds by default.Do not confuse them. Neither changes the time of an ordinary at job, and neither caps CPU use once a job is running.
For an SMP host, the manual suggests considering a load limit higher than the number of CPUs minus one. Choose a value from your workload rather than copying that suggestion blindly. A conservative foreground test can show the accepted syntax, but it is not a service configuration:
$ sudo atd -d -f -l 4.0 -b 120
Warning: Do not run that command while the systemd instance is active: it would create a competing daemon. It is only for a maintenance window where you have first stopped the unit and have a clear return plan.
-d prints errors to standard error and implies -f. The output wording is implementation-dependent, so use the journal and exit status as the useful checks.
To make a load or interval setting persistent, add the option to a systemd drop-in rather than editing the vendor unit.
Warning: This requires root and restarts atd, which briefly interrupts queue processing. Pending jobs remain in the spool, but a job whose time passes during the interruption may run later.
$ sudo systemctl edit atd.service
Enter this example, changing the values to match your host:
[Service]
ExecStart=
ExecStart=/usr/sbin/atd -f -l 4.0 -b 120
The empty ExecStart= is required before replacing an existing command in a systemd service. Save and exit, then verify the rendered unit before restarting:
$ systemctl cat atd.service
$ sudo systemctl daemon-reload
$ sudo systemctl restart atd.service
$ systemctl is-active atd.service
active
$ systemctl show atd.service -p ExecStart
Recovery: To undo this override, run sudo systemctl revert atd.service, then sudo systemctl daemon-reload and sudo systemctl restart atd.service. If the restart fails, inspect systemctl status atd.service and journalctl -u atd.service -b --no-pager, then revert the drop-in before retrying.
The installed manual identifies /var/spool/cron/atjobs as the job directory and /var/spool/cron/atspool as the output directory. Both should be mode 700 and owned by daemon. Check them without changing permissions:
$ stat -c '%A %U:%G %n' /var/spool/cron/atjobs /var/spool/cron/atspool
drwxrwx--T daemon:daemon /var/spool/cron/atjobs
drwxrwx--T daemon:daemon /var/spool/cron/atspool
The sticky-directory permissions shown are the installed Ubuntu package's arrangement.
Warning: Do not recursively chmod or chown these paths. An apparently tidy change can break submission or daemon access.
The manual also warns that atd will not work when its spool is mounted through NFS, even with no_root_squash. Keep the spool on suitable local storage, and check the mount before diagnosing a queue that never runs:
$ findmnt -T /var/spool/cron/atjobs -o TARGET,FSTYPE,SOURCE
$ findmnt -T /var/spool/cron/atspool -o TARGET,FSTYPE,SOURCE
atd.service is active, and it is the only daemon instance processing this host's queue.atq lists pending work and atrm can remove a test job before execution.systemctl cat atd.service.