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
- 2. Schedule library yang paling simpel
- 3. APScheduler untuk production
- 4. Contoh nyata: backup dan laporan
- 5. Jalan tanpa script hidup terus: Windows dan Linux
- 6. Tips biar scheduler tidak ngadat
1. Kapan Pakai Apa: schedule vs APScheduler vs Task Scheduler
jika kalian baru mulai, maka jangan langsung ke yang kompleks. pilih sesuai kebutuhan:
| Kebutuhan | Pakai | Kelebihan | Kekurangan |
|---|---|---|---|
| script jalan terus, jadwal simpel | schedule | ringan, satu file, syntax enak dibaca | butuh while True, tidak ada persistensi |
| jadwal kompleks, butuh cron, interval, tanggal | APScheduler | lengkap, bisa simpan job ke DB, ada BackgroundScheduler | agak berat, config lebih banyak |
| tanpa script hidup terus | Task Scheduler / cron | OS yang bangunin script, irit RAM | setup di luar Python |
aturan praktis saya di 2026:
- untuk automasi di laptop atau VPS kecil yang scriptnya memang jalan terus, pakai
scheduledulu - untuk API yang sudah pakai Flask atau FastAPI, pakai
APSchedulerdenganBackgroundScheduler - untuk backup harian yang cukup jalan sekali sehari, pakai Task Scheduler di Windows atau
crondi 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:
scheduleitu blocking diwhile 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.dbkekunci 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. pakailoggingbiar 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 loopschedule - cegah dobel, di APScheduler pakai
max_instances=1, dischedulejangan 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.atsetelah 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 butuhwhile Truedantime.sleep(1) - APScheduler itu lengkap, ada
BlockingScheduleruntuk script murni danBackgroundScheduleruntuk jalan bareng API, plusCronTriggerdanjobstore - Task Scheduler dan cron itu solusi tanpa script hidup terus, OS yang bangunin Python sesuai jadwal
- untuk backup dan laporan, bungkus job dengan
try-exceptdanlogging, lalu aturmax_instances=1biar 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.

