Back to guides

Guides · May 12, 2026

Python cron jobs: every way to schedule Python in 2026 (with code)

Four ways to schedule Python in production, with code for each. Pick crontab for a single box, APScheduler for in-process clocks, platform cron for serverless, or external HTTP cron when you want retries and alerts without an always-on process.
crontap.com / guides
Four ways to schedule Python: crontab, APScheduler, platform cron, and external HTTP cron. Code samples for Flask, FastAPI, and Django plus when to pick each.

Python has four credible ways to schedule work on a clock, and they cover different production realities. You can drop a script in crontab, run APScheduler inside a long-lived process, lean on a platform scheduler like AWS EventBridge, or expose an HTTP endpoint and let an external cron service like Crontap hit it on cadence.

This guide is the side-by-side walkthrough with code, plus when to pick which. For Linux crontab basics, see What is a cron job in Linux?.

Path 1: crontab -e on a Linux box

The classic shape: a script on disk and cron firing it.

# crontab -e
15 2 * * * /usr/bin/python3 /opt/app/nightly_sync.py >> /var/log/nightly_sync.log 2>&1

Use the full path to python3. Cron's PATH is tiny. If you use uv or poetry, wrap the command:

15 2 * * * cd /opt/app && /usr/local/bin/uv run python nightly_sync.py >> /var/log/nightly_sync.log 2>&1

Pick this when: one server, one team, SSH access, and you are fine grepping logs when something breaks at 3am.

Path 2: APScheduler or schedule in a long-running process

APScheduler runs inside your Python app. The process must stay up.

from apscheduler.schedulers.blocking import BlockingScheduler
 
def nightly_sync():
    # your job
 
scheduler = BlockingScheduler()
scheduler.add_job(nightly_sync, "cron", hour=2, minute=15)
scheduler.start()

The lighter schedule library fits the same model with a simpler API.

Pick this when: you already run a always-on worker and want scheduling colocated with app code. Avoid when: the process is allowed to die (serverless, spot instances, deploys that restart the box).

Path 3: Platform schedulers (Lambda, Cloud Run, Vercel)

Cloud vendors ship cron-shaped triggers:

  • AWS: EventBridge rule → Lambda (see cron jobs for AWS Lambda)
  • GCP: Cloud Scheduler → Cloud Run
  • Vercel: vercel.json crons (hourly floor on Hobby)

Pick this when: the workload already lives on that platform and you want billing tied to invocations. Watch for: IAM setup, cold starts, and per-platform timezone quirks.

Path 4: External HTTP cron (Crontap)

Deploy a small HTTP route that runs the job. Crontap fires it on schedule with retries and alerts.

Why this beats local cron for many production apps

  • No always-on Python process just to hold a scheduler
  • Retries on 5xx with backoff
  • Failure alerts to Slack, Discord, Telegram, or email
  • Per-schedule IANA timezones
  • Authorization headers stored in the dashboard, not in crontab

Flask example

import os
from flask import Flask, abort, request
 
app = Flask(__name__)
SECRET = os.environ["CRON_SECRET"]
 
@app.post("/internal/nightly-sync")
def nightly_sync():
    if request.headers.get("Authorization") != f"Bearer {SECRET}":
        abort(401)
    # run sync
    return "", 204

In Crontap: POST https://api.yourapp.com/internal/nightly-sync, header Authorization: Bearer <secret>, cron 15 2 * * *, timezone America/New_York.

FastAPI example

from fastapi import FastAPI, Header, HTTPException
 
app = FastAPI()
 
@app.post("/internal/nightly-sync")
async def nightly_sync(authorization: str = Header()):
    if authorization != f"Bearer {os.environ['CRON_SECRET']}":
        raise HTTPException(status_code=401)
    await run_sync()
    return {"ok": True}

Django management command as an endpoint

Expose a locked-down view that calls call_command("nightly_sync"). Keep the command idempotent (see below).

Fix this in 60 seconds with Crontap. Free forever tier. Three schedules. No credit card. Schedule your first job →

How to make a Python cron idempotent

Cron fires whether or not yesterday's run finished. Guard with:

  • A database lock row (SELECT ... FOR UPDATE)
  • A short-lived Redis key (SET sync:lock NX EX 3600)
  • Writing job state before side effects

If two runs overlap, the second should no-op cleanly, not double-charge customers.

How to monitor a Python cron with Crontap

FAQ

Can I use cron with uv or poetry?

Yes, but not bare python in crontab. cd to the project and invoke uv run or poetry run with full paths.

How do I handle long-running Python jobs?

Set a timeout in Crontap above your p99 runtime. For jobs longer than HTTP cron allows, split into enqueue + worker, or use a queue (Celery, RQ) triggered by a short HTTP handler.

How do I keep secrets out of crontab?

Use Path 4: secrets live in your app's environment; Crontap stores only the bearer token for the schedule.

Related on Crontap

Fix this in 60 seconds with Crontap. Free forever tier. Three schedules. No credit card. Schedule your first job →

From the blog

Read the blog

Guides, patterns and product updates.

Tutorials on scheduling API calls, webhooks and automations, plus deep dives into cron syntax, timezones and reliability.

Alternatives

Vercel Cron every minute: beating the Hobby hourly limit

Vercel Cron caps Hobby at hourly cadence and 5 jobs, and ties every change to a redeploy. Here is the external cron pattern teams use to ship per-minute schedules, per-IANA timezones, and one dashboard across projects without paying $20/mo per user for Pro.

Alternatives

Cloud Run cron without Cloud Scheduler

Cloud Scheduler costs $0.10 per job per month after the first 3 and asks for OIDC plus IAM bindings on every target. Here is the IAM-free pattern Cloud Run teams use to fire their .run.app URLs on a clock with one bearer token and one dashboard across every GCP project.