Recently, I’ve been working to migrate my own personal infrastructure off of Kubernetes, given that it has become quite expensive to run for just a single hosted website. For example, on Linode, the smallest cluster (3 nodes), along with a load balancer, and several disks, was approaching 50$ a month. Indeed, what did I actually use my cluster for? I used it for hosting one-off websites for a couple of days, and then tearing them down, I used it for my own personal website, and I used it for a host of CRON jobs that have to run every hour or every couple of days. The one-off websites I could easily migrate to vercel, and my personal website I migrated to Cloudflare Pages + static site generation (11ty), but the CRON jobs were a bit trickier. Luckily, I found Modal, a service for serverless compute which allows running compute on a schedule (with a bit of configuration).
All of my cron jobs were python scripts - so they already fit with Modal’s supported languages. The first thing to do
was to install the Modal pip package, which is as simple as running pip install modal
. Then sign up for an account on
the modal website: https://modal.com/. You can then log in using the CLI with modal setup
.
The next step was to create a model app. In the example, I’ll run a simple script which makes a GET request to a URL every hour.
import modal
import requests
app = modal.App(name="example-cron-job")
@app.function()
def main():
requests.get("https://example.com")
Unfortunately, that’s not enough, since “requests” is not a built-in python module. To fix this, we need to create an image on which Modal can run:
import modal
import requests
app = modal.App(name="example-cron-job")
image = modal.Image.debian_slim(python_version="3.10").pip_install("requests")
@app.function(image=image)
def main():
requests.get("https://example.com")
Now that we have the image, we can setup the schedule:
import modal
import requests
app = modal.App(name="example-cron-job")
image = modal.Image.debian_slim(python_version="3.10").pip_install("requests")
@app.function(image=image, schedule=modal.Cron("0 * * * *"))
def main():
requests.get("https://example.com")
Here, the schedule is set to run every hour. The final step is to deploy the app:
modal deploy example-cron-job.py
This will deploy the app to Modal’s infrastructure, and it will run every hour. The pricing is a bit hard to estimate, since jobs are chaged by CPU/Memory-seconds, however it comes with a pretty generous free tier of 30$/month, and in the 20-30 odd jobs that I’ve run so far, I haven’t even used 0.01$, so it’s quite cheap.