Schedule Python Scripts to Run Automatically: Complete Guide
You've built a Python script that saves hours of work. But you still have to remember to run it. Every day. Or every Monday. Or the first of each month.
That's not true automation. Let's fix it.
Today you'll learn to schedule scripts to run automatically—no human intervention required.
What You'll Learn
- Using Task Scheduler on Windows
- Using cron on macOS/Linux
- Python-based scheduling with schedule library
- Running scripts at startup
- Handling errors and logging for unattended execution
Prerequisites
- Python 3.8 or higher
- A Python script you want to schedule
- Administrator access to your computer
The Problem
Manual script execution means:
- You have to remember to run it
- If you're sick, on vacation, or busy—it doesn't run
- Timing is inconsistent
- You waste time on routine execution
The Solution
System schedulers that run your scripts automatically:
- Windows: Task Scheduler
- macOS/Linux: cron
- Cross-platform: Python schedule library or systemd
Method 1: Windows Task Scheduler
Task Scheduler is built into Windows and perfect for automation.
Step 1: Find Your Python Path
First, we need to know where Python is installed:
1where pythonCopy the path (e.g., C:\Users\YourName\AppData\Local\Programs\Python\Python311\python.exe)
Step 2: Create the Scheduled Task
- Press
Win + R, typetaskschd.msc, press Enter - Click Create Basic Task in the right panel
- Name: "Daily Report Generator" (or whatever describes your script)
- Description: "Runs the Python report script every day at 8 AM"
- Click Next
Step 3: Set the Trigger
- Choose when: Daily, Weekly, or Monthly
- Set the start time (e.g., 8:00 AM)
- For daily: set recurrence (every 1 day)
- Click Next
Step 4: Set the Action
- Select Start a program
- Click Next
- Program/script: Paste your Python path
C:\Users\YourName\AppData\Local\Programs\Python\Python311\python.exe - Add arguments: Full path to your script
"C:\Scripts\my_automation.py" - Start in (optional): Your script's directory
C:\Scripts - Click Next, then Finish
Step 5: Configure Additional Settings
- Find your task in Task Scheduler Library
- Right-click → Properties
- General tab: Check "Run whether user is logged on or not"
- Conditions tab: Uncheck "Start only if computer is on AC power" for laptops
- Settings tab: Check "Run task as soon as possible after a scheduled start is missed"
Testing Your Task
Right-click the task → Run to test immediately.
Method 2: macOS/Linux with cron
Cron is the standard scheduler for Unix systems.
Step 1: Open the Crontab Editor
1crontab -eThis opens your personal crontab in the default editor (usually nano or vim).
Step 2: Understand Cron Syntax
* * * * * command │ │ │ │ │ │ │ │ │ └── Day of week (0-7, where 0 and 7 are Sunday) │ │ │ └──── Month (1-12) │ │ └────── Day of month (1-31) │ └──────── Hour (0-23) └────────── Minute (0-59)
Step 3: Add Your Schedule
Common schedule examples:
1# Run every day at 8:00 AM
20 8 * * * /usr/bin/python3 /home/user/scripts/my_script.py
3
4# Run every Monday at 9:00 AM
50 9 * * 1 /usr/bin/python3 /home/user/scripts/weekly_report.py
6
7# Run every hour
80 * * * * /usr/bin/python3 /home/user/scripts/hourly_check.py
9
10# Run every 15 minutes
11*/15 * * * * /usr/bin/python3 /home/user/scripts/monitor.py
12
13# Run first day of every month at midnight
140 0 1 * * /usr/bin/python3 /home/user/scripts/monthly_report.py
15
16# Run Monday through Friday at 6 PM
170 18 * * 1-5 /usr/bin/python3 /home/user/scripts/end_of_day.pyStep 4: Save and Verify
Save the crontab (in nano: Ctrl+O, Enter, Ctrl+X)
Verify your crontab:
1crontab -lImportant: Use Full Paths
Cron runs in a minimal environment. Always use:
- Full path to Python:
/usr/bin/python3 - Full path to your script:
/home/user/scripts/my_script.py
Find your Python path:
1which python3Cron Output and Logging
By default, cron sends output via email. Redirect to a log file instead:
10 8 * * * /usr/bin/python3 /home/user/scripts/my_script.py >> /home/user/logs/my_script.log 2>&1The 2>&1 redirects errors to the same file.
Method 3: Python schedule Library
For cross-platform scheduling or when you need more control:
1pip install scheduleBasic Usage
1#!/usr/bin/env python3
2"""
3Scheduled Task Runner - Run functions on a schedule.
4Author: Alex Rodriguez
5"""
6
7import schedule
8import time
9from datetime import datetime
10
11
12def job_daily_report():
13 """Example job: Generate daily report."""
14 print(f"[{datetime.now()}] Running daily report...")
15 # Your automation code here
16
17
18def job_hourly_check():
19 """Example job: Hourly system check."""
20 print(f"[{datetime.now()}] Running hourly check...")
21 # Your automation code here
22
23
24def job_weekly_backup():
25 """Example job: Weekly backup."""
26 print(f"[{datetime.now()}] Running weekly backup...")
27 # Your automation code here
28
29
30# Schedule the jobs
31schedule.every().day.at("08:00").do(job_daily_report)
32schedule.every().hour.do(job_hourly_check)
33schedule.every().monday.at("06:00").do(job_weekly_backup)
34
35# Also supports:
36# schedule.every(10).minutes.do(job)
37# schedule.every().wednesday.at("13:15").do(job)
38# schedule.every().day.at("10:30:00").do(job) # With seconds
39
40
41def main():
42 """Run the scheduler."""
43 print("Scheduler started. Press Ctrl+C to exit.")
44 print(f"Scheduled jobs: {len(schedule.get_jobs())}")
45
46 for job in schedule.get_jobs():
47 print(f" - {job}")
48
49 while True:
50 schedule.run_pending()
51 time.sleep(60) # Check every minute
52
53
54if __name__ == "__main__":
55 main()Running schedule as a Service
The schedule library requires the Python script to keep running. Use it with:
Windows: Run as a background service with pythonw.exe
Linux/macOS: Run with nohup or as a systemd service
1# Keep running after terminal closes
2nohup python3 scheduler.py &Making Scripts Robust for Unattended Execution
When scripts run without supervision, they need proper error handling and logging.
Add Logging
1#!/usr/bin/env python3
2"""
3Robust Automation Script - With logging and error handling.
4Author: Alex Rodriguez
5"""
6
7import logging
8import sys
9from datetime import datetime
10from pathlib import Path
11
12
13# Set up logging
14def setup_logging(log_file=None):
15 """Configure logging for unattended execution."""
16
17 if log_file is None:
18 # Log to script directory
19 script_dir = Path(__file__).parent
20 log_file = script_dir / "automation.log"
21
22 logging.basicConfig(
23 level=logging.INFO,
24 format='%(asctime)s - %(levelname)s - %(message)s',
25 handlers=[
26 logging.FileHandler(log_file),
27 logging.StreamHandler(sys.stdout) # Also print to console
28 ]
29 )
30
31 return logging.getLogger(__name__)
32
33
34def main():
35 """Main function with error handling."""
36 logger = setup_logging()
37
38 logger.info("=" * 50)
39 logger.info("Script started")
40
41 try:
42 # Your automation code here
43 logger.info("Processing data...")
44
45 # Example: Simulate work
46 result = process_data()
47 logger.info(f"Processing complete: {result}")
48
49 logger.info("Script completed successfully")
50
51 except FileNotFoundError as e:
52 logger.error(f"File not found: {e}")
53 sys.exit(1)
54
55 except PermissionError as e:
56 logger.error(f"Permission denied: {e}")
57 sys.exit(1)
58
59 except Exception as e:
60 logger.exception(f"Unexpected error: {e}")
61 sys.exit(1)
62
63
64def process_data():
65 """Example processing function."""
66 return "100 records processed"
67
68
69if __name__ == "__main__":
70 main()Add Email Notifications for Failures
1import smtplib
2from email.mime.text import MIMEText
3
4
5def send_error_notification(error_message, script_name):
6 """Send email when script fails."""
7
8 sender = "alerts@yourcompany.com"
9 recipient = "admin@yourcompany.com"
10
11 msg = MIMEText(f"""
12 Script Failure Alert
13
14 Script: {script_name}
15 Time: {datetime.now()}
16 Error: {error_message}
17
18 Please investigate.
19 """)
20
21 msg['Subject'] = f"[ALERT] Script Failed: {script_name}"
22 msg['From'] = sender
23 msg['To'] = recipient
24
25 try:
26 with smtplib.SMTP('smtp.yourcompany.com', 587) as server:
27 server.starttls()
28 server.login(sender, "password")
29 server.send_message(msg)
30 except Exception as e:
31 logging.error(f"Could not send alert email: {e}")
32
33
34# In your main() function:
35try:
36 # Your code
37 pass
38except Exception as e:
39 logger.exception(f"Script failed: {e}")
40 send_error_notification(str(e), "my_script.py")
41 sys.exit(1)The Complete Robust Script Template
1#!/usr/bin/env python3
2"""
3Scheduled Automation Script Template
4Author: Alex Rodriguez
5
6A robust template for scripts that run unattended on a schedule.
7Includes logging, error handling, and optional notifications.
8"""
9
10import logging
11import os
12import sys
13from datetime import datetime
14from pathlib import Path
15
16# Configuration
17SCRIPT_NAME = "automation_script"
18LOG_RETENTION_DAYS = 30
19ENABLE_EMAIL_ALERTS = False
20ALERT_EMAIL = "admin@company.com"
21
22
23def setup_logging():
24 """Set up logging with rotation."""
25 # Create logs directory
26 log_dir = Path(__file__).parent / "logs"
27 log_dir.mkdir(exist_ok=True)
28
29 # Log file with date
30 log_file = log_dir / f"{SCRIPT_NAME}_{datetime.now():%Y%m%d}.log"
31
32 # Configure logging
33 logging.basicConfig(
34 level=logging.INFO,
35 format='%(asctime)s | %(levelname)-8s | %(message)s',
36 datefmt='%Y-%m-%d %H:%M:%S',
37 handlers=[
38 logging.FileHandler(log_file, encoding='utf-8'),
39 logging.StreamHandler(sys.stdout)
40 ]
41 )
42
43 logger = logging.getLogger(SCRIPT_NAME)
44
45 # Clean old logs
46 cleanup_old_logs(log_dir)
47
48 return logger
49
50
51def cleanup_old_logs(log_dir, days=LOG_RETENTION_DAYS):
52 """Remove log files older than specified days."""
53 import time
54
55 cutoff = time.time() - (days * 86400)
56
57 for log_file in log_dir.glob("*.log"):
58 if log_file.stat().st_mtime < cutoff:
59 log_file.unlink()
60
61
62def send_alert(subject, message):
63 """Send an alert email (implement based on your email setup)."""
64 if not ENABLE_EMAIL_ALERTS:
65 return
66
67 # Add your email sending code here
68 # See our email automation guide for full implementation
69 pass
70
71
72def validate_environment():
73 """Check that required resources are available."""
74 logger = logging.getLogger(SCRIPT_NAME)
75
76 # Example: Check required directories exist
77 required_dirs = ["data", "output"]
78 script_dir = Path(__file__).parent
79
80 for dir_name in required_dirs:
81 dir_path = script_dir / dir_name
82 if not dir_path.exists():
83 logger.warning(f"Creating missing directory: {dir_path}")
84 dir_path.mkdir(exist_ok=True)
85
86 # Example: Check required files exist
87 # required_files = ["config.json"]
88 # for file_name in required_files:
89 # if not (script_dir / file_name).exists():
90 # raise FileNotFoundError(f"Required file missing: {file_name}")
91
92 return True
93
94
95def run_automation():
96 """
97 Main automation logic.
98
99 CUSTOMIZE THIS FUNCTION for your specific task.
100 """
101 logger = logging.getLogger(SCRIPT_NAME)
102
103 # Your automation code goes here
104 logger.info("Starting automation task...")
105
106 # Example steps:
107 # 1. Read input data
108 logger.info("Step 1: Reading input data")
109 # data = read_input()
110
111 # 2. Process data
112 logger.info("Step 2: Processing data")
113 # results = process(data)
114
115 # 3. Save output
116 logger.info("Step 3: Saving output")
117 # save_output(results)
118
119 # Return summary
120 return {
121 "status": "success",
122 "records_processed": 100,
123 "output_file": "output/results.xlsx"
124 }
125
126
127def main():
128 """Main entry point with full error handling."""
129
130 # Initialize logging
131 logger = setup_logging()
132
133 logger.info("=" * 60)
134 logger.info(f"STARTING: {SCRIPT_NAME}")
135 logger.info(f"Time: {datetime.now()}")
136 logger.info("=" * 60)
137
138 exit_code = 0
139
140 try:
141 # Validate environment
142 validate_environment()
143
144 # Run the automation
145 result = run_automation()
146
147 # Log success
148 logger.info("-" * 60)
149 logger.info("COMPLETED SUCCESSFULLY")
150 logger.info(f"Result: {result}")
151 logger.info("-" * 60)
152
153 except FileNotFoundError as e:
154 logger.error(f"File not found: {e}")
155 send_alert(f"[ERROR] {SCRIPT_NAME}", f"File not found: {e}")
156 exit_code = 1
157
158 except PermissionError as e:
159 logger.error(f"Permission denied: {e}")
160 send_alert(f"[ERROR] {SCRIPT_NAME}", f"Permission denied: {e}")
161 exit_code = 1
162
163 except Exception as e:
164 logger.exception(f"Unexpected error: {e}")
165 send_alert(f"[CRITICAL] {SCRIPT_NAME}", f"Unexpected error: {e}")
166 exit_code = 1
167
168 finally:
169 logger.info(f"Script finished with exit code: {exit_code}")
170 logger.info("=" * 60 + "\n")
171
172 sys.exit(exit_code)
173
174
175if __name__ == "__main__":
176 main()How to Run This Script
Windows Task Scheduler Setup
- Save the script as
C:\Scripts\automation_script.py - Open Task Scheduler
- Create Basic Task with these settings:
- Program:
C:\Users\You\AppData\Local\Programs\Python\Python311\python.exe - Arguments:
"C:\Scripts\automation_script.py" - Start in:
C:\Scripts
- Program:
Linux/macOS Cron Setup
1# Edit crontab
2crontab -e
3
4# Add line (runs every day at 8 AM):
50 8 * * * /usr/bin/python3 /home/user/scripts/automation_script.pyCommon Issues & Solutions
| Issue | Solution |
|---|---|
| Script doesn't run | Check full paths for Python and script |
| "ModuleNotFoundError" | Install packages for the correct Python version |
| Permission denied | Run as administrator or check file permissions |
| Script runs but no output | Check working directory; use absolute paths |
| Missed schedules | Check "Run as soon as possible after missed" setting |
Taking It Further
Run at System Startup
Windows: Create a .bat file in the Startup folder:
1@echo off
2pythonw.exe "C:\Scripts\my_script.py"Linux: Add to /etc/rc.local or create a systemd service
Create a Systemd Service (Linux)
1# /etc/systemd/system/my-automation.service
2[Unit]
3Description=My Python Automation
4After=network.target
5
6[Service]
7Type=simple
8User=youruser
9WorkingDirectory=/home/youruser/scripts
10ExecStart=/usr/bin/python3 /home/youruser/scripts/scheduler.py
11Restart=always
12
13[Install]
14WantedBy=multi-user.targetEnable it:
1sudo systemctl enable my-automation
2sudo systemctl start my-automationConclusion
Your Python scripts shouldn't need babysitting. With proper scheduling, error handling, and logging, they become truly autonomous tools that work for you around the clock.
Start with one script. Get it running reliably on a schedule. Then expand to more. Before long, you'll have an army of automated processes handling your routine tasks while you focus on work that matters.
True automation runs itself.
Set it and forget it.
Sponsored Content
Interested in advertising? Reach automation professionals through our platform.