Belajar Python Scheduler: Automasi Task dengan Schedule dan APScheduler 2026

| | Python
Belajar Python Scheduler: Automasi Task dengan Schedule dan APScheduler 2026

automasi adalah alasan utama saya belajar Python, dan menjadwalkan task adalah bagian yang membuatnya benar-benar berguna. backup database setiap malam, laporan otomatis setiap pagi, semuanya berjalan tanpa perlu diingat 🙂. kita mulai dari cara paling sederhana, sampai yang bisa dijadwalkan otomatis di Windows dan Linux.

jika kalian sudah paham manipulasi waktu di Python dan file handling, maka materi scheduler ini akan langsung nyambung. soalnya hampir semua task yang dijadwalkan ujungnya baca file, tulis laporan, atau backup database.

Daftar Isi

1. Kapan Pakai Apa: schedule vs APScheduler vs Task Scheduler

jika kalian baru mulai, maka jangan langsung ke yang kompleks. pilih sesuai kebutuhan:

KebutuhanPakaiKelebihanKekurangan
script jalan terus, jadwal simpelscheduleringan, satu file, syntax enak dibacabutuh while True, tidak ada persistensi
jadwal kompleks, butuh cron, interval, tanggalAPSchedulerlengkap, bisa simpan job ke DB, ada BackgroundScheduleragak berat, config lebih banyak
tanpa script hidup terusTask Scheduler / cronOS yang bangunin script, irit RAMsetup di luar Python

aturan praktis saya di 2026:

  • untuk automasi di laptop atau VPS kecil yang scriptnya memang jalan terus, pakai schedule dulu
  • untuk API yang sudah pakai Flask atau FastAPI, pakai APScheduler dengan BackgroundScheduler
  • untuk backup harian yang cukup jalan sekali sehari, pakai Task Scheduler di Windows atau cron di Linux, lebih hemat

2. Schedule Library yang Paling Simpel

jika ingin cepat jadi, maka schedule adalah yang paling ramah pemula:

pip install schedule
import schedule
import time
from datetime import datetime

def backup():
    print(f"[{datetime.now():%Y-%m-%d %H:%M:%S}] Backup database...")

def laporan():
    print(f"[{datetime.now():%H:%M}] Generate laporan harian")

# jadwal
schedule.every(10).minutes.do(backup)
schedule.every().hour.do(backup)
schedule.every().day.at("08:00").do(laporan)
schedule.every().monday.at("09:00").do(laporan)
schedule.every().day.at("08:00").do(backup)

print("scheduler jalan, tekan Ctrl+C untuk berhenti")
while True:
    schedule.run_pending()
    time.sleep(1)

schedule.every(10).minutes.do(backup) itu bacanya natural, tiap 10 menit jalankan backup. every().day.at("08:00") tiap hari jam 8 pagi. format jam harus "HH:MM" 24 jam.

sedikit trik yang sering kepakai:

import schedule

# dengan parameter
def sapa(nama):
    print(f"Halo {nama}")

schedule.every().day.at("07:00").do(sapa, nama="Budi")

# tag biar bisa dibatalkan
schedule.every().hour.do(backup).tag("backup")
# nanti bisa schedule.clear("backup")

# jadwal tiap detik untuk testing, jangan di production
schedule.every(5).seconds.do(backup)

jika scriptnya harus tahan lama, maka jangan lupa logging dan handle error, kalau tidak satu error bikin loop berhenti:

import schedule
import time
import logging

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s")

def job_aman():
    try:
        # pekerjaan utama
        print("kerja...")
        # raise ValueError("contoh error")
    except Exception as e:
        logging.error(f"job gagal: {e}", exc_info=True)

schedule.every().day.at("08:00").do(job_aman)

while True:
    try:
        schedule.run_pending()
    except Exception as e:
        logging.error(f"scheduler error: {e}", exc_info=True)
    time.sleep(1)

catatan saya: schedule itu blocking di while True, jadi kalau ada job yang lama (misal backup 5 menit), job lain akan nunggu. untuk job lama, jalankan di thread terpisah atau pakai APScheduler.

versi non-blocking simpel dengan thread:

import schedule
import time
import threading

def job_lama():
    print("mulai job lama...")
    time.sleep(10)
    print("selesai")

def run_threaded(job):
    threading.Thread(target=job, daemon=True).start()

schedule.every(10).seconds.do(run_threaded, job_lama)

while True:
    schedule.run_pending()
    time.sleep(1)

3. APScheduler untuk Production

jika butuh yang lebih serius, maka APScheduler adalah standar 2026. di versi 3.10 ke atas syntax tetap sama, hanya dependency tzlocal sudah lebih stabil:

pip install apscheduler
# untuk jobstore database
pip install sqlalchemy

BlockingScheduler vs BackgroundScheduler

from apscheduler.schedulers.blocking import BlockingScheduler
from datetime import datetime

sched = BlockingScheduler(timezone="Asia/Jakarta")

@sched.scheduled_job('cron', hour=8, minute=0)
def laporan_harian():
    print(f"[{datetime.now()}] Generate laporan...")

@sched.scheduled_job('interval', hours=24)
def backup_db():
    print(f"[{datetime.now()}] Backup...")

# cron lebih fleksibel
@sched.scheduled_job('cron', day_of_week='mon-fri', hour=9, minute=30)
def weekday_job():
    print("khusus senin sampai jumat jam 9:30")

print("APScheduler blocking start, Ctrl+C untuk stop")
sched.start()  # blocking, baris bawah tidak akan jalan

BlockingScheduler cocok untuk script yang memang cuma scheduler. kalau mau jalan bareng API, pakai BackgroundScheduler:

from apscheduler.schedulers.background import BackgroundScheduler
import time

sched = BackgroundScheduler(timezone="Asia/Jakarta")
sched.start()

# tambah job manual
def halo():
    print("halo dari background")

sched.add_job(halo, 'interval', seconds=10, id="halo_job", replace_existing=True)

# API atau loop utama tetap jalan
print("scheduler jalan di background, API tetap hidup")
try:
    while True:
        time.sleep(1)
except KeyboardInterrupt:
    sched.shutdown()

Trigger lengkap APScheduler

from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger
from datetime import datetime, timedelta

sched = BackgroundScheduler(timezone="Asia/Jakarta")
sched.start()

# interval: tiap 10 menit
sched.add_job(lambda: print("interval 10m"), 'interval', minutes=10, id="int10")

# cron: tiap hari jam 8
sched.add_job(lambda: print("cron 08:00"), CronTrigger(hour=8, minute=0), id="cron8")

# date: sekali jalan 10 detik lagi
run_at = datetime.now() + timedelta(seconds=10)
sched.add_job(lambda: print("sekali jalan"), 'date', run_date=run_at, id="once")

# cron kompleks: tiap senin jam 7 pagi
sched.add_job(lambda: print("senin 07:00"), CronTrigger(day_of_week='mon', hour=7, minute=0), id="mon7")

Cegah dobel dan simpan ke database

from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore

jobstores = {
    'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite')
}
sched = BackgroundScheduler(jobstores=jobstores, timezone="Asia/Jakarta")
sched.start()

# max_instances=1 cegah job yang belum selesai dipanggil lagi
# coalesce=True kalau terlewat beberapa kali, jalan cuma sekali
# misfire_grace_time=300 toleransi 5 menit kalau scheduler sempat mati
sched.add_job(
    lambda: print("job aman"),
    'interval', minutes=5,
    id="aman",
    max_instances=1,
    coalesce=True,
    misfire_grace_time=300,
    replace_existing=True
)

jika pakai SQLAlchemyJobStore, maka job tidak hilang saat restart. cocok untuk VPS yang kadang reboot. file jobs.sqlite akan kesimpan di folder project, sama seperti yang dibahas di SQLite.

4. Contoh Nyata: Backup dan Laporan

jika sudah paham trigger, maka ini contoh yang sering saya pakai di project nyata:

import schedule
import time
import shutil
from pathlib import Path
from datetime import datetime
import logging

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s")

SRC = Path("data.db")
BACKUP_DIR = Path("backup")
BACKUP_DIR.mkdir(exist_ok=True)

def backup_database():
    try:
        if not SRC.exists():
            logging.warning("data.db tidak ada, skip backup")
            return
        nama = BACKUP_DIR / f"backup_{datetime.now():%Y%m%d_%H%M%S}.db"
        shutil.copy2(SRC, nama)
        # hapus backup lebih dari 7 hari biar tidak penuh
        for f in BACKUP_DIR.glob("backup_*.db"):
            if (datetime.now().timestamp() - f.stat().st_mtime) > 7*24*3600:
                f.unlink()
                logging.info(f"hapus backup lama {f.name}")
        logging.info(f"backup sukses {nama.name}")
    except Exception as e:
        logging.error(f"backup gagal: {e}", exc_info=True)

def laporan_harian():
    try:
        # contoh baca sqlite lalu tulis laporan txt
        import sqlite3
        with sqlite3.connect(SRC) as conn:
            cur = conn.execute("SELECT COUNT(*) FROM notes" )
            total = cur.fetchone()[0]
        out = Path(f"laporan_{datetime.now():%Y%m%d}.txt")
        out.write_text(f"Tanggal: {datetime.now():%Y-%m-%d}\nTotal notes: {total}\n", encoding="utf-8")
        logging.info(f"laporan dibuat {out.name}")
    except Exception as e:
        logging.error(f"laporan gagal: {e}", exc_info=True)

schedule.every().day.at("02:00").do(backup_database)
schedule.every().day.at("08:00").do(laporan_harian)

# untuk testing, jalan tiap menit
# schedule.every().minute.do(backup_database)

print("scheduler laporan jalan")
while True:
    schedule.run_pending()
    time.sleep(1)

jika mau pakai APScheduler untuk contoh yang sama:

from apscheduler.schedulers.blocking import BlockingScheduler
import shutil
from pathlib import Path
from datetime import datetime

SRC = Path("data.db")
BACKUP_DIR = Path("backup")
BACKUP_DIR.mkdir(exist_ok=True)

def backup_database():
    nama = BACKUP_DIR / f"backup_{datetime.now():%Y%m%d_%H%M%S}.db"
    if SRC.exists():
        shutil.copy2(SRC, nama)
        print(f"backup {nama}")

sched = BlockingScheduler(timezone="Asia/Jakarta")
sched.add_job(backup_database, 'cron', hour=2, minute=0, id="backup")
sched.add_job(lambda: print("laporan"), 'cron', hour=8, minute=0, id="laporan")
sched.start()

tip saya: untuk backup, selalu cek file hasil backup bisa dibuka. saya pernah backup tiap malam tapi ternyata data.db kekunci dan hasil copy corrupt, baru ketahuan saat butuh restore.

5. Jalan Tanpa Script Hidup Terus: Windows dan Linux

jika kalian tidak mau script while True hidup terus, maka serahkan ke OS:

Windows Task Scheduler

buat file backup.py yang sekali jalan selesai:

# backup.py
import shutil
from pathlib import Path
from datetime import datetime

shutil.copy2("data.db", Path(f"backup_{datetime.now():%Y%m%d}.db"))
print("backup selesai")

buat file .bat:

@echo off
cd /d C:\project\automasi
C:\project\automasi\.venv\Scripts\python.exe backup.py >> backup.log 2>&1

lalu jadwalkan tanpa GUI pakai schtasks:

# tiap hari jam 02:00
schtasks /create /tn "BackupDB" /tr "C:\project\automasi\run_backup.bat" /sc daily /st 02:00 /f

# tiap senin jam 08:00
schtasks /create /tn "Laporan" /tr "C:\project\automasi\run_laporan.bat" /sc weekly /d MON /st 08:00 /f

# cek dan hapus
schtasks /query /tn "BackupDB"
schtasks /delete /tn "BackupDB" /f

atau lewat GUI: buka Task Scheduler → Create Task → tab Triggers → Daily jam 02:00 → Actions → Start a program → pilih .bat → centang Run whether user is logged on or not.

Linux cron

# buka crontab
crontab -e

# tiap hari jam 2 pagi
0 2 * * * /home/user/project/.venv/bin/python /home/user/project/backup.py >> /home/user/project/backup.log 2>&1

# tiap senin jam 8 pagi
0 8 * * 1 /home/user/project/.venv/bin/python /home/user/project/laporan.py

# cek log cron
grep CRON /var/log/syslog

jika pakai Docker di 2026, maka cron bisa dimasukkan ke image:

FROM python:3.12-slim
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
RUN apt-get update && apt-get install -y cron
COPY crontab /etc/cron.d/app-cron
RUN chmod 0644 /etc/cron.d/app-cron && crontab /etc/cron.d/app-cron
CMD ["cron", "-f"]

6. Tips Biar Scheduler Tidak Ngadat

jika scheduler sudah jalan di production, maka hal ini yang sering bikin kaget:

  • beri logging, jangan cuma print. pakai logging biar ada timestamp dan level, dan tulis ke file biar bisa dilacak seperti di error handling
  • handle error di dalam job, jangan biarkan exception keluar tanpa try-except, satu job error bisa hentikan loop schedule
  • cegah dobel, di APScheduler pakai max_instances=1, di schedule jangan jadwal terlalu rapat kalau job-nya lama
  • beri timeout untuk koneksi database dan request HTTP, biar job tidak gantung selamanya
  • test dengan interval detik dulu, baru ganti ke every().day.at setelah yakin
  • pasang sebagai service kalau butuh jalan setelah reboot. di Windows pakai Task Scheduler dengan Run at startup, di Linux pakai systemd:
# /etc/systemd/system/automasi.service
[Unit]
Description=Python Scheduler
After=network.target

[Service]
WorkingDirectory=/home/user/project
ExecStart=/home/user/project/.venv/bin/python scheduler.py
Restart=always

[Install]
WantedBy=multi-user.target
sudo systemctl enable automasi
sudo systemctl start automasi
sudo systemctl status automasi

jika butuh presisi detik atau butuh kalender kompleks, maka APScheduler lebih tepat. jika cuma butuh tiap jam atau harian, schedule sudah cukup dan kodenya enak dibaca tim.

Kesimpulan

  • schedule itu ringan, syntax every().day.at("08:00") paling enak dibaca, tapi butuh while True dan time.sleep(1)
  • APScheduler itu lengkap, ada BlockingScheduler untuk script murni dan BackgroundScheduler untuk jalan bareng API, plus CronTrigger dan jobstore
  • Task Scheduler dan cron itu solusi tanpa script hidup terus, OS yang bangunin Python sesuai jadwal
  • untuk backup dan laporan, bungkus job dengan try-except dan logging, lalu atur max_instances=1 biar tidak dobel
  • di 2026, pasang scheduler sebagai service biar tahan restart, dan test dulu dengan interval detik sebelum pakai 02:00

habis ini kalian bisa kombinasikan dengan manipulasi waktu buat format tanggal laporan, atau SQLite buat simpan hasil automasi.

Baca Juga Mengenai :

Pertanyaan yang Sering Diajukan

Apa itu scheduler di Python?

scheduler itu penjadwal yang jalanin fungsi otomatis pada waktu tertentu, misalnya tiap jam atau tiap jam 8 pagi. di Python yang paling populer adalah schedule untuk yang ringan dan APScheduler untuk yang butuh fitur lengkap.

Apa beda schedule dan APScheduler?

schedule itu simpel, satu file, cocok untuk script yang jalan terus dengan while True. APScheduler lebih lengkap, bisa cron, interval, tanggal, plus simpan job ke database dan cegah job tabrakan.

Kenapa schedule butuh while True dan time.sleep?

karena schedule tidak punya loop sendiri. kalian harus panggil schedule.run_pending tiap detik. time.sleep bikin CPU tidak 100 persen.

Bagaimana jalankan task tanpa script harus hidup terus?

pakai penjadwal OS seperti Windows Task Scheduler atau cron di Linux. script Python cukup dijalankan sesuai jadwal oleh OS, jadi tidak perlu while True.

APScheduler bisa jalan bareng Flask atau FastAPI?

bisa, pakai BackgroundScheduler bukan BlockingScheduler. BackgroundScheduler jalan di thread terpisah, jadi API tetap responsif.

Bagaimana cegah job dobel kalau eksekusi lama?

di APScheduler set max_instances=1 dan coalesce=True, plus misfire_grace_time. di schedule, atur agar job tidak dipanggil lagi sebelum selesai.

Bagaimana biar jadwal tetap jalan setelah restart?

untuk schedule, perlu service atau Task Scheduler. untuk APScheduler, pakai jobstore seperti SQLAlchemyJobStore biar job kesimpan di database, dan pasang sebagai service.

Sigit Nurhanafi avatar
Full-stack developer & technical writer. Berpengalaman di PHP, Laravel, NodeJS, MySQL, dan Python. Aktif menulis tutorial pemrograman dan maintaining open-source projects di PemburuKode sejak 2021.