What is at?
The at command schedules one-time tasks to run at a specific time in the future. Unlike cron which runs recurring tasks, at is for jobs you want to run once. Need to restart a service at 2 AM tonight? Want to send yourself a reminder in 3 hours? The at command is your tool. It's managed by the atd daemon (at daemon) which must be running on your system.
Example 1Schedule a Simple Command
echo "echo 'Backup complete' >> /tmp/backup.log" | at 2:30 AM
Schedule a command to run at 2:30 AM today (or tomorrow if it's already past 2:30 AM). The command will append a message to a log file.
warning: commands will be executed using /bin/sh
job 1 at Thu Nov 2 02:30:00 2025
Note: The job number (1 in this case) is important for managing the scheduled task later.
Example 2Interactive Mode
at 10:00 PM
Enter interactive mode where you can type multiple commands. Press Ctrl+D when finished to save the job.
at> /home/user/scripts/backup.sh at> echo "Backup started at $(date)" | mail -s "Backup Notice" admin@example.com at> <EOT> job 2 at Thu Nov 2 22:00:00 2025
Pro Tip: Interactive mode is great for complex tasks with multiple commands. Each line is a separate command.
Example 3Schedule with Relative Time
echo "systemctl restart apache2" | at now + 30 minutes
Schedule a command to run 30 minutes from now. Relative time specifications are incredibly flexible and natural.
job 3 at Thu Nov 2 14:45:00 2025
Time Formats: now + 1 hour, now + 2 days, now + 3 weeks, now + 1 month, now + 1 year
Example 4View Pending Jobs
atq
List all pending at jobs. Shows job number, scheduled time, queue, and user. The command 'atq' is short for "at queue".
1 Thu Nov 2 02:30:00 2025 a craig
2 Thu Nov 2 22:00:00 2025 a craig
3 Thu Nov 2 14:45:00 2025 a craig
Pro Tip: You can also use 'at -l' which does the same thing as atq.
Example 5View Job Details
at -c 3
Display the full details and commands for job number 3. This shows the complete script that will be executed, including environment variables.
#!/bin/sh
# atrun uid=1000 gid=1000
# mail craig 0
umask 22
# ... environment variables ...
cd /home/craig || {
echo 'Execution directory inaccessible' >&2
exit 1
}
systemctl restart apache2
Example 6Remove a Scheduled Job
atrm 3
Remove (delete) job number 3 from the queue. Use this to cancel scheduled tasks you no longer need.
Pro Tip: You can also use 'at -r 3' or 'at -d 3' to remove jobs. All three commands do the same thing.
Example 7Schedule from a Script File
at 3:00 AM -f /home/user/scripts/nightly_backup.sh
Execute an entire script file at 3:00 AM. The -f flag tells at to read commands from a file instead of stdin.
job 4 at Fri Nov 3 03:00:00 2025
Note: The script must be readable by the at daemon. Make sure permissions are correct!
Example 8Specific Date and Time
echo "reboot" | at 2:00 AM 12/25/2025
Schedule a task for a specific date and time. Useful for planned maintenance windows or scheduled events.
job 5 at Wed Dec 25 02:00:00 2025
Warning: Be careful with reboot commands! Make sure this is what you really want to do.
Example 9Natural Time Specifications
echo "tar -czf /backup/home.tar.gz /home" | at midnight
Use natural language time specifications. "midnight", "noon", "teatime" (4 PM), "tomorrow", "next week", etc.
job 6 at Fri Nov 3 00:00:00 2025
Fun Fact: "teatime" = 4:00 PM. Very British, but it works on all Linux systems!
Example 10Schedule for Tomorrow
echo "df -h | mail -s 'Disk Space Report' admin@example.com" | at 9:00 AM tomorrow
Explicitly schedule for tomorrow at 9:00 AM. Perfect for setting up morning reports or maintenance tasks.
job 7 at Fri Nov 3 09:00:00 2025
Example 11Schedule for Next Week
echo "apt update && apt upgrade -y" | at 11:00 PM next Tuesday
Schedule system updates for next Tuesday at 11:00 PM. You can specify days of the week with relative terms.
job 8 at Tue Nov 7 23:00:00 2025
Example 12Use Different Queue
echo "nice -n 19 /usr/bin/updatedb" | at -q b now + 1 hour
Use the -q flag to specify a different queue (a-z, with 'a' being highest priority, 'z' lowest). Lower priority jobs are "nicer".
job 9 at Thu Nov 2 15:30:00 2025
Note: Queue 'a' is default. Use lower priority queues (b, c, etc.) for resource-intensive tasks.
Example 13Batch Processing Queue
echo "/home/user/process_large_files.sh" | batch
The 'batch' command is a special form of 'at' that runs when system load is low (typically below 0.8). Perfect for CPU-intensive tasks.
job 10 at Thu Nov 2 14:30:00 2025
Pro Tip: batch is equivalent to 'at -q b now'. It waits for low system load before executing.
Example 14Multiple Commands at Once
at now + 5 minutes << END cd /var/log gzip *.log mv *.gz /backup/logs/ echo "Log compression complete" | logger END
Use here-document syntax (<<) to schedule multiple commands in one go. Everything between 'END' markers becomes the job.
job 11 at Thu Nov 2 14:35:00 2025
Example 15Check atd Service Status
systemctl status atd
Verify that the at daemon is running. The at command won't work if atd isn't active.
● atd.service - Deferred execution scheduler
Loaded: loaded (/lib/systemd/system/atd.service; enabled)
Active: active (running) since Thu 2025-11-02 08:00:00 EST
Main PID: 1234 (atd)
Tasks: 1
Memory: 1.2M
CGroup: /system.slice/atd.service
└─1234 /usr/sbin/atd -f
Warning: If atd is not running, start it with: sudo systemctl start atd
📚 Additional Information
Time Specification Examples
| Format | Example | Description |
|---|---|---|
| HH:MM | 14:30 | 2:30 PM today (or tomorrow if past) |
| HH:MM AM/PM | 2:30 PM | 2:30 PM with explicit AM/PM |
| midnight | midnight | 12:00 AM |
| noon | noon | 12:00 PM |
| teatime | teatime | 4:00 PM |
| now + time | now + 3 hours | 3 hours from now |
| tomorrow | 10:00 AM tomorrow | Tomorrow at 10:00 AM |
| next week | next Monday | Next occurrence of Monday |
| MMDDYY | 122525 | December 25, 2025 |
| MM/DD/YYYY | 12/25/2025 | December 25, 2025 |
Common at Command Options
| Option | Description |
|---|---|
| -f file | Read commands from file instead of stdin |
| -q queue | Use specified queue (a-z) |
| -l | List pending jobs (same as atq) |
| -d | Delete jobs (same as atrm) |
| -r | Remove jobs (same as atrm) |
| -c job | Show job details |
| -m | Send mail even if no output |
| -M | Never send mail |
| -v | Show time job will execute |
Related Commands
| Command | Purpose |
|---|---|
| at | Schedule one-time tasks |
| atq | List pending at jobs |
| atrm | Remove at jobs |
| batch | Schedule low-priority tasks (runs when load permits) |
| cron | Schedule recurring tasks |
| systemd timers | Modern alternative to cron |
Access Control
The at command uses two files to control access:
- /etc/at.allow - If exists, only users listed here can use at
- /etc/at.deny - If at.allow doesn't exist, users listed here cannot use at
- If neither file exists, only root can use at (most restrictive)
- If only at.deny exists and is empty, all users can use at (most permissive)
Real-World Use Cases
- Scheduled Reboots: Restart systems during maintenance windows
- Delayed Processing: Start resource-intensive tasks during off-hours
- Reminders: Send yourself notifications at specific times
- Temporary Tasks: One-time backups or data processing
- Testing: Schedule test scripts to run at specific times
- Auto-cleanup: Delete temporary files after a delay
Best Practices
- Use absolute paths: at jobs may have different PATH variables
- Test first: Schedule test jobs in 1-2 minutes to verify they work
- Check atq regularly: Monitor pending jobs to avoid surprises
- Use batch for heavy tasks: Prevents system overload
- Mail notifications: at will email output by default (if mail is configured)
- Document your jobs: Add comments in scripts for future reference
Troubleshooting
Problem: "You do not have permission to use at"
Solution: Check /etc/at.allow and /etc/at.deny. Add your username to at.allow or remove from at.deny
Solution: Check /etc/at.allow and /etc/at.deny. Add your username to at.allow or remove from at.deny
Problem: Jobs not executing
Solution: Check if atd is running: systemctl status atd. Start it if needed: sudo systemctl start atd
Solution: Check if atd is running: systemctl status atd. Start it if needed: sudo systemctl start atd
Problem: "Can't open /var/run/atd.pid"
Solution: atd is not running. Start the service: sudo systemctl start atd
Solution: atd is not running. Start the service: sudo systemctl start atd
Problem: Job ran but nothing happened
Solution: Check your mail for error messages: mail. Jobs output is emailed by default.
Solution: Check your mail for error messages: mail. Jobs output is emailed by default.
Environment Variables
at jobs inherit the environment from when they were scheduled, including:
- Current directory (cd to specific directory if needed)
- Environment variables (SHELL, PATH, HOME, etc.)
- User permissions (jobs run as the user who scheduled them)
at vs. cron: When to Use Which?
| Use at when... | Use cron when... |
|---|---|
| Task runs once | Task runs repeatedly |
| Specific date/time | Regular schedule (daily, weekly, etc.) |
| Ad-hoc scheduling | Permanent automation |
| Delayed execution | Routine maintenance |
| Testing/temporary tasks | Production automation |
Advanced Examples
# Schedule script with output logging
echo "/home/user/backup.sh >> /var/log/backup.log 2>&1" | at 2:00 AM
# Chain multiple at jobs
echo "echo '/second/script.sh' | at now + 2 hours" | at now + 1 hour
# Conditional execution
echo "[ -f /tmp/ready ] && /usr/local/bin/process.sh" | at now + 30 minutes
# Schedule with multiple queues for prioritization
echo "critical_task.sh" | at -q a now + 10 minutes # High priority
echo "normal_task.sh" | at -q b now + 10 minutes # Normal priority
echo "low_priority.sh" | at -q c now + 10 minutes # Low priority
# Schedule system maintenance
cat <<EOF | at 3:00 AM Sunday
apt update
apt upgrade -y
apt autoremove -y
systemctl reboot
EOF
# Monitor and alert if disk space low
echo "df -h | awk '\$5 > 90 {print}' | mail -s 'Disk Alert' admin@example.com" | at now + 1 hour
# Delayed cleanup after deployment
echo "rm -rf /tmp/deployment_files/*" | at now + 24 hours
# Schedule database backup at specific time
echo "mysqldump -u root -p'password' mydb > /backup/mydb_\$(date +%Y%m%d).sql" | at 11:30 PM
Security Considerations
- File permissions: Ensure scripts called by at have appropriate permissions
- Path safety: Use absolute paths to avoid PATH hijacking
- Credential management: Don't hardcode passwords in at jobs; use credential files with proper permissions
- Review jobs: Regularly check atq to ensure no unauthorized jobs are scheduled
- Audit logging: Monitor /var/log/syslog for at job execution
Practical Sysadmin Scenarios
# Delayed service restart after configuration change
echo "systemctl restart nginx" | at now + 5 minutes
# Schedule certificate renewal for 3 AM
echo "certbot renew --quiet" | at 3:00 AM tomorrow
# Test website response time
echo "curl -o /dev/null -s -w '%{time_total}\n' https://example.com" | at now + 15 minutes
# Schedule firewall rule removal (temporary access)
echo "iptables -D INPUT -s 192.168.1.100 -j ACCEPT" | at now + 2 hours
# Automated log rotation
echo "gzip /var/log/application.log && mv /var/log/application.log.gz /backup/logs/" | at midnight
# System health check before maintenance
echo "/usr/local/bin/pre-maintenance-check.sh" | at 11:00 PM Friday
Integration with Other Tools
# Schedule with notify-send (desktop notification)
echo "notify-send 'Backup Complete' 'Your scheduled backup has finished'" | at now + 3 hours
# Integration with logger
echo "logger -t at-job 'Scheduled maintenance complete'" | at 4:00 AM
# Send Slack notification
echo "curl -X POST -H 'Content-type: application/json' --data '{\"text\":\"Scheduled task complete\"}' SLACK_WEBHOOK_URL" | at now + 1 hour
# Update monitoring system
echo "curl -X POST https://monitoring.example.com/checkin/job-123" | at now + 30 minutes
Pro Tip for Beginners: Start with simple tasks scheduled 1-2 minutes in the future. Use atq to watch them appear, and check your mail for output. This builds confidence before scheduling critical tasks.
Pro Tip for Sysadmins: Create a wrapper script that logs all at job activity. This helps with troubleshooting and provides an audit trail for compliance.
Fun Facts
- The 'at' command dates back to Unix Version 7 (1979)
- "teatime" as a time specification is a nod to the British tradition of afternoon tea
- The 'batch' command is essentially 'at' with load-aware scheduling
- at jobs survive reboots - they're stored in /var/spool/cron/atjobs/
- The output of at jobs is emailed to you automatically (if mail is configured)