Search articles

Start typing to search article titles.

How to run tasks periodically in Render.com

By Jose Salvatierra
How to run tasks periodically in Render.com

In our last newsletter we talked about task queues using rq, and while it is possible to add periodic tasks to a queue, there’s a simpler way!

The recommended way to run tasks periodically is to use a cron job. Most web hosting providers, like Heroku, don’t allow you to run cron jobs very easily. You can run cron jobs if you own the server, like when using DigitalOcean, but that brings its own set of challenges.

Fortunately for us, Render.com has a “Cron Job” service type that you can use to spin up a web server specifically to run your cron jobs. After the cron job finishes running, the server is destroyed. This lowers the cost and makes it quite affordable. I’ve been paying about $1 per month per cron job. With Render.com, you pay based on how long the cron job takes to run.

When setting up a Cron Job in Render.com, you get to clone an existing GitHub repository and set up the schedule and the command. The schedule defines how often the Cron Job runs, and the command defines what should run.

Cron Job schedules look a bit weird:

*/60 * * * *

This schedule runs every 60 minutes.

The five asterisks each mean:

  • Minute
  • Hour
  • Day of the month
  • Month
  • Day of the week
  • (optionally, one more) Year

You can use the asterisk to mean “every one”. So every minute, or every hour. The forward slash, like in */60, means “every 60”. In this case, every 60 minutes. You could also put a specific number, such as 27 * * * * to mean every 27th minute of every hour (so, once per hour).

There’s quite a few other symbols you can use, so here’s a handy cron schedule expression generator!

What I use cron jobs for

I’ve got a web service running in Render.com, and cron jobs can use the same code repositories as web services. My web services run continuously, and then I use cron jobs to collect data from the web service and send emails.

In one very specific example, a web service is used to show the team at Teclado how many pending unanswered questions are left in our courses. Having this information helps us stay on top of answering all our questions.

The cron job, every day, sends me an email with how many unanswered questions we’ve got left.

Like I mentioned, you could do this with task queues but then you’d need a Redis database and a background worker process. And you’d need to integrate the task queue into the web application. Overall, much more complicated and also more expensive.

This is what my cron job code looks like:

import os
import pymongo
import requests
from postmarker.core import PostmarkClient
from dotenv import load_dotenv

load_dotenv()

mongo_client = pymongo.MongoClient(
    os.environ.get("MONGODB_URI")
)
db = mongo_client.get_default_database()

unread_qa_count = db.daily_unreads.find_one(sort=[("timestamp", pymongo.DESCENDING)])[
    "amount"
]

email_body = f"""<p>Hi!</p>

<p>Right now there are {unread_qa_count} unread questions across all courses.</p>

<p>Check out more details at <a href="https://qa.teclado.com">https://qa.teclado.com</a>.</p>"""

postmark = PostmarkClient(server_token=os.getenv("POSTMARK_SERVER_API_TOKEN"))
postmark.emails.send(
    From="myemail@mydomain.com",
    To="myemail@mydomain.com",
    Subject=f"{unread_qa_count} Unread Q&A (Daily update)",
    HtmlBody=email_body,
    MessageStream="outbound",
)

Here’s what it does:

  • Connects to our MongoDB database.
  • Fetches the latest unread questions count from the database.
  • Constructs an HTML email.
  • Sends it using Postmark (not using Mailgun here, but I’m going to migrate to it since I use Mailgun for other stuff).

Of course, you’re not limited to sending emails. You could do anything using a cron job, such as run models, verify the health of services, purge databases, or anything else.

Do you have any periodic tasks you’d like to run? Have you tried cron jobs? I’d love to hear more about what you are using. Please use the comments section below!