You wrote a Python script. It works. Now you find yourself opening a terminal every morning to run it by hand, and on the mornings you forget, nothing happens. That gets old fast.
The fix is scheduling. A scheduler is basically an alarm clock for your code. You tell it "run this script every day at 6am," and it does, whether or not you remember. In this guide you will learn how to schedule a Python script to run automatically, on any operating system, using the three approaches that actually matter. No prior DevOps knowledge needed.
If you built the pipeline from my beginner ETL guide, this is the piece that turns it from a script you babysit into something that runs on its own.
What "scheduling" actually means
A scheduler is just a program whose whole job is to run other programs at set times. cron and Windows Task Scheduler are schedulers built into your operating system. They sit quietly in the background and wake up to launch your script when the clock matches.
Here is the one mental shift that clears up most confusion. Your script does not need to be running for a scheduler to run it. The scheduler starts a brand new copy at the scheduled time, that copy runs to the end and exits, and tomorrow the scheduler starts another fresh copy. So a scheduled script should do its job once and finish, not loop forever waiting for the clock. The clock is the scheduler's job, not your script's.
Option 1: cron (Mac and Linux)
cron is the scheduler built into every Mac and Linux system. You hand it a schedule and a command, and it runs that command on that schedule, in the background, indefinitely.
You edit your schedule with one command:
crontab -eThat opens your personal list of scheduled jobs, called your crontab. Each line is one job. The format looks cryptic the first time, but it is only five time fields followed by the command to run.
So to run a script every day at 6:00am:
# minute hour day month weekday command
0 6 * * * /home/you/venv/bin/python /home/you/pipeline.py >> /home/you/pipeline.log 2>&1Let me unpack that command, because the details are exactly where people get stuck:
0 6 * * *is minute 0 of hour 6, on every day, month and weekday. That is 6 daily./home/you/venv/bin/pythonis the full path to the Python inside your virtual environment, not justpython. cron knows nothing about your venv, so you point straight at it./home/you/pipeline.pyis the full path to your script.>> /home/you/pipeline.log 2>&1sends everything the script prints, including errors, into a log file so you can actually see what happened.
A handful of schedules you will reach for constantly:
| Schedule | Cron line | Meaning |
|---|---|---|
| Every day at 6am | 0 6 * * * | minute 0, hour 6, any day |
| Every hour | 0 * * * * | at the top of every hour |
| Every 15 minutes | */15 * * * * | the */15 means "every 15" |
| Every Monday at 9am | 0 9 * * 1 | weekday 1 is Monday |
| Midnight on the 1st | 0 0 1 * * | day of month 1 |
If cron syntax makes your eyes cross, crontab.guru turns any schedule into plain English as you type it. I still keep it open.
Option 2: Task Scheduler (Windows)
Windows has no cron, but it has Task Scheduler, which does the same job with a wizard instead of a text file. The short version:
- Press the Windows key, type Task Scheduler, and open it.
- Click Create Basic Task and give it a name like "Daily pipeline."
- Choose a trigger. Pick Daily and set your time.
- For the action, choose Start a program.
- In Program/script, put the full path to your venv's Python, for example
C:\Users\you\venv\Scripts\python.exe. - In Add arguments, put the full path to your script,
C:\Users\you\pipeline.py. - Finish.
The task now runs on the schedule you picked. Tick "run whether the user is logged on or not" on the last screen if it should run even when you are signed out.
Option 3: the schedule library (inside Python)
Sometimes you want the schedule to live in your code rather than in the operating system. Maybe you are on a host where you cannot touch cron, or you just want everything in one place. The schedule library is the friendliest way to do that.
pip install scheduleimport schedule
import time
def job():
print("Running the pipeline...")
# your real work goes here, e.g. run_pipeline()
# say what to run and when, almost in plain English
schedule.every().day.at("06:00").do(job)
schedule.every(15).minutes.do(job)
# keep the program alive so it can trigger jobs on time
while True:
schedule.run_pending()
time.sleep(1)There is a catch, and it matters. This only works while the program is running. That while True loop is the program staying awake to watch the clock. If the process stops, the schedule stops with it. So schedule shines when it lives inside something already running long-term, and it is a poor fit for "run once a day then exit," where cron or Task Scheduler are the honest answer.
So which one should you use?
| If you... | Use |
|---|---|
| are on Mac or Linux and want "run and exit" | cron |
| are on Windows | Task Scheduler |
| want the schedule inside a long-running Python app | the schedule library |
| need restarts, parallelism or persistence | APScheduler, or an orchestrator |
For most beginners scheduling a data script, cron on a Linux server or Task Scheduler on a Windows machine is the entire answer. Start there and add complexity only when something forces you to.
The 5 gotchas that silently break scheduled scripts
This is the part I wish someone had handed me on day one. A script that runs perfectly by hand can fail the instant a scheduler runs it, and it tends to fail quietly. Here is why, roughly in order of how often it bites people.
- Relative paths. Your script opens
data.csvand it works from your project folder, but the scheduler runs from somewhere else entirely, so the file is not found. Use absolute paths, or build them from the script's own location. - The wrong Python. cron and Task Scheduler default to the system Python, which does not have the packages you
pip installed. Point them at your virtual environment's Python instead. - No logging. When a scheduled run fails, there is no terminal watching to show you the error. Redirect output to a log file (
>> file.log 2>&1in cron) or add Python'slogging, so failures leave a trace you can read later. - The script waits for input. Any
input()call, or a library that pops a prompt, will hang forever because nobody is there to answer. An automated script has to run start to finish with zero interaction. - Timezone surprises. The server's clock may not be your clock. A job set for "6am" can fire at 6am UTC, which might be the middle of your night. Check the machine's timezone before you trust the time.
Get these five right and most scheduling pain simply disappears.
When to graduate from a scheduler
cron and Task Scheduler are great for a handful of independent jobs. You outgrow them when jobs start depending on each other (run the report only after the load finishes), when you need automatic retries and alerts on failure, or when you are juggling dozens of them and cron turns into a wall of cryptic lines nobody dares touch.
That is when teams move to an orchestrator like Dagster, Airflow or Prefect, which add dependencies, retries, and a dashboard that shows what ran and what broke. I compared the three in Dagster vs Airflow vs Prefect. On a distributor pipeline I built for an FMCG client, jobs kicked off automatically when files landed on a server, not on a fixed clock, which is the kind of thing an orchestrator makes easy and cron cannot really do. You do not need any of that on day one. You just need to recognise the day cron stops being enough.
Frequently asked questions
How do I run a Python script automatically every day?
On Mac or Linux, add a cron job with crontab -e and a line like 0 6 * * * /path/to/venv/bin/python /path/to/script.py. On Windows, use Task Scheduler's Create Basic Task wizard with a Daily trigger. Both launch a fresh copy of your script at the set time.
How do I schedule a Python script on Windows?
Open Task Scheduler, choose Create Basic Task, set a Daily or other trigger, then for the action pick "Start a program" and point it at your virtual environment's python.exe, with your script's full path as the argument. Tick "run whether user is logged on or not" if it should run in the background.
cron or the schedule library, which is better?
cron is better for "run once and exit" jobs, because the operating system handles it and nothing of yours has to stay running. The schedule library is better when the schedule lives inside an app that is already running long-term. If you need persistence across restarts or parallel jobs, use APScheduler instead.
Why does my scheduled Python script fail when it runs fine manually?
Almost always one of three things: it uses relative paths that break from the scheduler's working directory, it runs with the system Python that lacks your installed packages, or it errors silently with no log to explain why. Use absolute paths, point at your venv's Python, and write output to a log file.
The takeaway
Scheduling is the small step that turns a script into a system. You do not need a fancy tool for it. cron on Mac and Linux, Task Scheduler on Windows, or the schedule library inside a running app will each take a job off your plate for good. The tool matters less than the habits: full paths, the right Python, and logs you can actually read when something breaks.
Set it up once, and the thing you used to run by hand just happens on its own. That is a genuinely good feeling.
If you are wiring up real data pipelines and trying to work out where a cron line ends and a proper orchestrator begins, that is the kind of work I do. Or tell me what you are automating and I will point you the right way.
Mirza Hammad Tariq
AWS Data Engineer with 5+ years building production-grade ETL pipelines, cloud data warehouses, and scalable data architectures in Python, SQL, Dagster, and AWS.