You’ve written the script. It works. Now what?
Running scripts manually defeats the purpose of automation. The real power comes when your scripts run themselves – at 3 AM when you’re asleep, every hour for monitoring, or whenever a specific event occurs.
Linux uses cron. Windows uses Task Scheduler. Both accomplish the same goal: executing code on a schedule without human intervention.
Automation starts with scheduling. I use cron jobs across my entire infrastructure — backup scripts, certificate renewals, monitoring checks, DuckDNS updates. If you’re doing something manually on a regular basis, it should probably be a cron job.
Career Impact
Scheduled automation is what separates reactive admins from proactive engineers.
When you can set up reliable, self-running maintenance, monitoring, and reporting, you’re demonstrating the operational maturity that hiring managers look for in Senior Sysadmin, DevOps, and SRE roles paying £55k-80k+.
What You’ll Learn
- Cron syntax and scheduling on Linux
- Task Scheduler configuration on Windows
- Common scheduling patterns
- Error handling and notifications
- Best practices for scheduled tasks
Quick Reference
Cron Time Fields
* * * * * command
| | | | |
| | | | +-- Day of week (0-7, Sun=0 or 7)
| | | +---- Month (1-12)
| | +------ Day of month (1-31)
| +-------- Hour (0-23)
+---------- Minute (0-59)
Common Cron Schedules
| Schedule | Cron Expression | Description |
|---|---|---|
| Every minute | * * * * * |
Testing only |
| Every hour | 0 * * * * |
On the hour |
| Daily at midnight | 0 0 * * * |
Backups, reports |
| Daily at 3 AM | 0 3 * * * |
Maintenance |
| Every Monday | 0 0 * * 1 |
Weekly tasks |
| First of month | 0 0 1 * * |
Monthly tasks |
| Every 15 minutes | */15 * * * * |
Frequent checks |
Cron on Linux
Understanding Crontab
Cron reads schedules from crontab files. Each user can have their own crontab, plus system-wide crontabs.
User crontab commands:
# Edit your crontab
crontab -e
# List your crontab
crontab -l
# Remove your crontab (dangerous)
crontab -r
System crontabs:
/etc/crontab– System crontab (includes user field)/etc/cron.d/– Drop-in directory for packages/etc/cron.daily/– Scripts run daily/etc/cron.hourly/– Scripts run hourly/etc/cron.weekly/– Scripts run weekly/etc/cron.monthly/– Scripts run monthly
Your First Cron Job
crontab -e
Add this line:
# Run backup script every day at 2 AM
0 2 * * * /home/user/scripts/backup.sh >> /var/log/backup.log 2>&1
Breaking it down:
0 2 * * *– At 2:00 AM every day/home/user/scripts/backup.sh– Full path to script>> /var/log/backup.log– Append stdout to log2>&1– Also capture stderr
Cron Special Strings
Instead of five fields, use shortcuts:
@reboot # Run once at startup
@yearly # Same as 0 0 1 1 *
@monthly # Same as 0 0 1 * *
@weekly # Same as 0 0 * * 0
@daily # Same as 0 0 * * *
@hourly # Same as 0 * * * *
Example:
@daily /home/user/scripts/cleanup.sh
@reboot /home/user/scripts/startup-tasks.sh
Environment Considerations
Cron runs with a minimal environment. Common issues:
Problem: Script works manually but not in cron.
Solution: Use full paths everywhere:
Practitioner tip: The most common cron mistake: your script works perfectly when you run it manually but fails silently in cron. That’s usually because cron runs with a minimal PATH. Always use absolute paths to commands in your cron scripts, or set PATH explicitly at the top.
# Bad (might not find commands)
0 3 * * * mysqldump mydb > backup.sql
# Good (explicit paths)
0 3 * * * /usr/bin/mysqldump mydb > /home/user/backups/backup.sql
Or set PATH in crontab:
PATH=/usr/local/bin:/usr/bin:/bin
0 3 * * * backup-script.sh
Cron Logging
View cron execution logs:
# Debian/Ubuntu
grep CRON /var/log/syslog
# RHEL/CentOS
grep CRON /var/log/cron
# Recent entries
journalctl -u cron --since "1 hour ago"
Practical Cron Examples
# Log rotation - daily at 1 AM
0 1 * * * /usr/sbin/logrotate /etc/logrotate.conf
# Database backup - Full backup Sunday at 2 AM, incremental other days
0 2 * * 0 /opt/scripts/full-backup.sh
0 2 * * 1-6 /opt/scripts/incremental-backup.sh
# Disk space monitoring - Check every 30 minutes
*/30 * * * * /opt/scripts/check-disk.sh
# Certificate renewal - Try renewal twice daily (certbot handles skip if not needed)
0 0,12 * * * /usr/bin/certbot renew --quiet
Task Scheduler on Windows
Accessing Task Scheduler
Server Manager > Tools > Task Scheduler
Or run: taskschd.msc
Creating a Basic Task (GUI Method)
- Right-click “Task Scheduler Library” > “Create Basic Task”
- Name and description
- Choose trigger (Daily, Weekly, etc.)
- Set time
- Choose action (Start a program)
- Browse to script/executable
- Finish
Creating an Advanced Task
For more control, use “Create Task” instead of “Create Basic Task”:
General Tab:
- Name and description
- Security options (run as different user)
- “Run whether user is logged on or not”
- “Run with highest privileges” (for admin tasks)
Triggers Tab:
- Schedule (daily, weekly, etc.)
- On startup, on logon, on event
- Repeat task every X minutes/hours
Actions Tab:
- Start a program
- For PowerShell scripts:
- Program:
powershell.exe - Arguments:
-ExecutionPolicy Bypass -File "C:\Scripts\script.ps1"
- Program:
Conditions Tab:
- Start only if on AC power
- Wake computer to run task
- Network conditions
Settings Tab:
- Allow task to run on demand
- Stop task if runs longer than X
- If task fails, restart every X minutes
PowerShell Task Management
Create tasks programmatically:
# Create a scheduled task
$action = New-ScheduledTaskAction -Execute 'powershell.exe' `
-Argument '-ExecutionPolicy Bypass -File "C:\Scripts\backup.ps1"'
$trigger = New-ScheduledTaskTrigger -Daily -At '3:00 AM'
$settings = New-ScheduledTaskSettingsSet -StartWhenAvailable
Register-ScheduledTask -TaskName "Daily Backup" `
-Action $action `
-Trigger $trigger `
-Settings $settings `
-User "SYSTEM" `
-RunLevel Highest
Multiple triggers:
$trigger1 = New-ScheduledTaskTrigger -At '6:00 AM' -Daily
$trigger2 = New-ScheduledTaskTrigger -At '6:00 PM' -Daily
Register-ScheduledTask -TaskName "Twice Daily Check" `
-Action $action `
-Trigger $trigger1, $trigger2
View and manage tasks:
# List all tasks
Get-ScheduledTask
# Get specific task
Get-ScheduledTask -TaskName "Daily Backup"
# Run task now
Start-ScheduledTask -TaskName "Daily Backup"
# Disable task
Disable-ScheduledTask -TaskName "Daily Backup"
# Remove task
Unregister-ScheduledTask -TaskName "Daily Backup" -Confirm:$false
Task Scheduler Triggers
| Trigger Type | Use Case |
|---|---|
| On a schedule | Regular maintenance |
| At startup | Services, monitoring |
| At logon | User-specific tasks |
| On idle | Low-priority background tasks |
| On an event | React to log entries |
| On workstation lock/unlock | Security auditing |
| On connect/disconnect | Session management |
Side-by-Side Comparison
Daily Script at 3 AM
Cron
0 3 * * * /opt/scripts/daily-task.sh >> /var/log/daily-task.log 2>&1
Task Scheduler (PowerShell)
$action = New-ScheduledTaskAction -Execute 'powershell.exe' `
-Argument '-File "C:\Scripts\daily-task.ps1"'
$trigger = New-ScheduledTaskTrigger -Daily -At '3:00 AM'
Register-ScheduledTask -TaskName "Daily Task" -Action $action -Trigger $trigger


