# Building a Cron Job Wrapper for Django

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

71 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
      1. Schedule/Repeats
      2. Datetimes
      3. Intervals
      4. Body
        1. Name
        2. Script⠀to⠀run
        3. Log⠀files⠀(info/err)
    • Wait⠀for⠀the⠀specified⠀interval
      • Check⠀the⠀current⠀time
      • Wait⠀until⠀the⠀scheduled⠀time
    • Execution⠀of⠀the⠀command
      • Captures⠀the⠀output
      • Potentially⠀handle⠀errors⠀
    • Manage
      • Edit/Update
      • Delete
      • Stop/Resume

    ---

    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

      ---

      It's⠀definitely⠀not⠀the⠀simplest⠀solution.

      But⠀at⠀least⠀it⠀gives⠀me⠀a⠀small⠀Python-based⠀framework⠀for⠀managing⠀CRON⠀jobs⠀without⠀introducing⠀another⠀database-backed⠀scheduling⠀service.

      And,⠀honestly,⠀that's⠀kind⠀of⠀the⠀whole⠀point⠀of⠀this⠀project.

      ⠀
      ⠀