# Building a Cron Job Wrapper for Django

By silverfish on Sept. 19, 2026, 9:54 a.m. · 10 min read

5 Totalviews
Automation Cron Django Python
HTML View MD View

So⠀lately,⠀I⠀have⠀been⠀fiddling⠀with⠀cron⠀jobs⠀and⠀I⠀decided⠀to⠀take⠀it⠀a⠀step⠀further⠀and⠀write⠀a⠀wrapper⠀around⠀them...

What⠀is⠀CRON?

A⠀CRON⠀is⠀a⠀daemon⠀used⠀to⠀execute⠀scheduled⠀commands.⠀You⠀can⠀set⠀the⠀schedule⠀for⠀when⠀a⠀script⠀or⠀command⠀should⠀run⠀and⠀let⠀the⠀process⠀run⠀at⠀the⠀desired⠀intervals.

  • Schedule⠀a⠀command
    • Define⠀the⠀command
    • Define⠀when⠀it⠀should⠀run
  • Wait⠀for⠀the⠀specified⠀interval
    • Check⠀the⠀current⠀time
    • Wait⠀until⠀the⠀scheduled⠀time
  • Execute⠀the⠀command
    • Capture⠀the⠀output
    • Handle⠀errors
  • Repeat

---

The⠀Original⠀Idea

The⠀rationale⠀behind⠀this⠀decision⠀was⠀that⠀I⠀wanted⠀to⠀automate⠀the⠀process⠀of⠀sending⠀emails.

Specifically,⠀the⠀email⠀handling⠀for⠀this⠀blog⠀among⠀other⠀things.

At⠀the⠀start,⠀I⠀considered⠀creating⠀an⠀SMTP⠀server,⠀but⠀after⠀some⠀digging,⠀I⠀found⠀out⠀that⠀the⠀web⠀hosting⠀service⠀I'm⠀using⠀does⠀not⠀recommend⠀it.

Although,⠀since⠀then⠀I⠀changed⠀providers⠀so⠀I⠀looking⠀forward⠀to⠀it!

The⠀idea⠀would⠀eventually⠀look⠀something⠀like⠀this:

```
┌───────────────┐
│    Django     │
│   Application │
└───────┬───────┘
        │
        ▼
┌───────────────┐
│ CRON Storage  │
└───────┬───────┘
        │
        ▼
┌───────────────┐
│  CRON Wrapper │
│    (Python)   │
└───────┬───────┘
        │
        ▼
┌───────────────┐
│ System CRON   │
└───────┬───────┘
        │
        ▼
┌───────────────┐
│    Script     │
└───────┬───────┘
        │
        ▼
      Email
```

Also,⠀in⠀the⠀past,⠀I⠀used⠀a⠀similar⠀pip⠀package⠀which⠀was⠀supposed⠀to⠀work⠀with⠀Django,⠀but⠀unfortunately,⠀I⠀couldn't⠀make⠀it⠀work.

---

Why⠀Create⠀Another⠀CRON⠀Service?

So,⠀the⠀CRON⠀service⠀I⠀was⠀planning⠀to⠀create...

(because⠀I⠀was⠀too⠀bored⠀to⠀simply⠀create⠀a⠀simple⠀CRON⠀after⠀SSHing⠀into⠀the⠀server)

...was⠀supposed⠀to⠀be⠀able⠀to⠀parse⠀CRON⠀jobs⠀from⠀the⠀DB⠀into⠀JSON⠀or⠀Python,⠀register⠀the⠀CRON⠀to⠀the⠀system,⠀and⠀hopefully⠀run⠀it.

Why⠀keep⠀the⠀3⠀places,⠀you⠀ask?

Good⠀question.

Because⠀who⠀doesn't⠀need⠀another⠀point⠀of⠀failure?

Obviously.

---

The⠀Three⠀Layers

The⠀general⠀idea⠀was⠀to⠀keep⠀the⠀CRON⠀information⠀in⠀three⠀different⠀places:

  1. Database
    • Stores⠀the⠀CRON⠀configuration.
    • Stores⠀information⠀about⠀the⠀job.
    • Potentially⠀stores⠀logs.

2.⠀JSON⠀/⠀Python⠀representation

  • Used⠀to⠀translate⠀the⠀DB⠀representation⠀into⠀something⠀executable.
  • Acts⠀as⠀an⠀intermediate/main⠀representation.

3.⠀System⠀CRON

Something⠀roughly⠀like:

```python
CronService(
    Schedule(),
    Script(),
    Commands(),
    Logs()
)
```

The⠀wrapper⠀would⠀then⠀be⠀responsible⠀for⠀converting⠀that⠀request⠀into⠀an⠀actual⠀system⠀CRON.

Why⠀Keep⠀Everything⠀Separate?

The⠀main⠀reason⠀is⠀that,⠀in⠀this⠀format,⠀I⠀could⠀post⠀CRON⠀jobs⠀to⠀the⠀server⠀I⠀wanted⠀and⠀let⠀them⠀run.

This⠀means⠀the⠀Django⠀application⠀doesn't⠀necessarily⠀have⠀to⠀be⠀responsible⠀for⠀actually⠀executing⠀the⠀task.

Instead,⠀it⠀can⠀simply⠀manage⠀the⠀configuration.

Django⠀manages⠀the⠀jobs.
Control⠀over⠀posting⠀jobs
CRON⠀executes⠀the⠀jobs.

---

Rate⠀Limiting

The⠀service⠀was⠀also⠀supposed⠀to⠀be⠀used⠀as⠀a⠀rate-limiting⠀measure⠀in⠀combination⠀with⠀actual⠀rate⠀limits⠀on⠀the⠀endpoint.

The⠀idea⠀is⠀to⠀create⠀a⠀single⠀point⠀where⠀I⠀can⠀carefully⠀manage⠀how⠀many⠀emails⠀are⠀being⠀sent.

For⠀example:

```text     
                 ┌──────────────┐
                 │ Rate Limit   │
                 └──────┬───────┘
                        │
                        ▼
                 ┌──────────────┐
                 │ Django API   │
                 └──────┬───────┘
                        │
                        ▼
                 ┌──────────────┐
                 │ Rate Limit   │
                 └──────┬───────┘
                        │
                        ▼
                 ┌──────────────┐
                 │ CRON Queue   │
                 └──────┬───────┘
                        │
                        ▼
                 ┌──────────────┐
                 │ Email Worker │
                 └──────────────┘
```

This⠀would⠀make⠀it⠀possible⠀to⠀control⠀the⠀number⠀of⠀emails⠀being⠀sent⠀instead⠀of⠀allowing⠀every⠀request⠀to⠀immediately⠀trigger⠀an⠀email.

---

Current⠀Progress

After⠀some⠀time⠀fiddling⠀with⠀the⠀code,⠀I⠀managed⠀to⠀scrap⠀together⠀a⠀small⠀framework⠀for⠀managing⠀CRONs.

Currently,⠀I'm⠀at⠀the⠀point⠀where⠀I⠀can⠀sufficiently:

  • [x]⠀Create⠀CRONs⠀from⠀various⠀formats
  • [x]⠀Delete⠀CRONs
  • [x]⠀Register⠀CRONs
  • [x]⠀Execute⠀CRONs
  • [x]⠀Manually⠀verify⠀that⠀jobs⠀can⠀run
  • [⠀]⠀Gather⠀logs
  • [⠀]⠀Verify⠀job⠀results⠀automatically
  • [⠀]⠀Integrate⠀everything⠀with⠀Django

The⠀implementation⠀is⠀still⠀evolving,⠀but⠀the⠀basic⠀functionality⠀is⠀there.

---

The⠀Hard⠀Part

I⠀have⠀manually⠀verified⠀that⠀the⠀jobs⠀can⠀run.

The⠀hard⠀part,⠀however,⠀is⠀verifying⠀that⠀the⠀CRON⠀is⠀actually⠀doing⠀what⠀it⠀is⠀supposed⠀to⠀do⠀without⠀using⠀any⠀automations.

Running⠀the⠀script⠀is⠀easy.

Knowing⠀whether⠀the⠀script⠀actually⠀accomplished⠀its⠀intended⠀purpose⠀is⠀a⠀different⠀art:

```text
CRON
  │
  ▼
Script
  │
  ├── Did it run?          ✓
  │
  ├── Did it crash?        ?
  │
  ├── Did the task work?   ?
  │
  └── Did the result make
      sense?                ?
```

I⠀suppose⠀a⠀solution⠀could⠀be⠀that⠀each⠀time⠀the⠀CRON⠀is⠀called,⠀it⠀can⠀check⠀on⠀itself.

But⠀that's⠀a⠀thought⠀for⠀later.

---

The⠀next⠀step⠀is⠀making⠀the⠀system⠀more⠀reliable.

In⠀particular,⠀I⠀need⠀to⠀figure⠀out:

  1. How⠀to⠀properly⠀gather⠀logs
  2. How⠀to⠀detect⠀failed⠀jobs
  3. How⠀to⠀verify⠀that⠀a⠀job⠀actually⠀completed⠀its⠀intended⠀task
  4. How⠀to⠀integrate⠀the⠀wrapper⠀cleanly⠀with⠀Django
  5. How⠀to⠀handle⠀jobs⠀that⠀appear⠀successful⠀but⠀actually⠀fail⠀somewhere⠀internally

---