Scheduling Netlify deploys with GitHub Actions

Using cron jobs on GitHub Actions and Netlify build hooks to update our site every night

Static websites don’t update by themselves. In case of code changes (git push) and content changes (CMS publish event) a user triggers an update. But what if an external system (regularly) updates but can’t be configured to trigger an update? Enter cron jobs.

Cron jobs

Cron jobs are a way to schedule actions to occur at a given time or at a given interval, without human intervention. The word “Cron” stems from Χρόνος (Chronos), the ancient Greek word for “time”. To continue quoting Wikipedia, in pre-socratic literature, Chronos is staged as the personification of time itself, and is often displayed as an old, wise man with a long grey beard. Getting back to the subject matter, cron is also the name of the program which, on unix-like systems, allows you to set up scripts or commands to be run periodically on a given schedule. Its syntax looks something like this:

* * * * * echo "the mysteries of time"

Which is saying: “Print the string “the mysteries of time” to stdout every minute of every hour of every day of every month of every year.

Although not as much of a mystery as time itself, it’s understandable that you may be a bit puzzled by cron’s syntax. Luckily, there is documentation available and there are many free online tools that do a good job providing you with ready-to-use cron statements. One of my favourites is The gist is:

┌──────── minute (0-59)
│ ┌──────── hour (0-23)
│ │ ┌──────── day of the month (1-31)
│ │ │ ┌──────── month (1-12 or JAN-DEC)
│ │ │ │ ┌──────── day of the week (0-6 or SUN-SAT)                             
│ │ │ │ │
* * * * *

Of course, you could do something slightly more useful than simply echo-ing out to the terminal as well. Say you want to deploy your Netlify project every night at a certain time. A “nightly build”, so to speak. Maybe data from an upstream source will change at a certain time each night, which has to be reflected in your statically generated website. For this, a scheduled build is perfect. However, Netlify does not provide this functionality out of the box.

Netlify Build Hooks

What Netlify does provide, is the ability to call a build hook URL to trigger a build. You can configure build hooks in your Netlify project’s config panel for your site, via Settings > Build & Deploy > Build Hooks. You give it a name and specify the branch to build, and you receive a URL that you can hit to start a new build:

Build Hooks in Netlify Build & Deploy settings
Build Hooks in Netlify Build & Deploy settings

Now you can trigger a new Netlify deploy directly from the command line using:

curl -X POST "{token}

This URL, or at least the part that identifies your project, should be handled as sensitive data to avoid unauthorised parties from starting new builds of your site.

GitHub Actions

Setting up your cron job to call the build hook URL can be done conveniently at the same place where you keep your code. Github Actions (v2) is currently still in public beta, but since it supports a cron scheduler, it serves our purpose already! Let’s set up a job that builds our Netlify project at 05:30 AM every night.

Extract the token from the Netlify build hook URL:{token}

Go to Settings > Secrets of your Github repo and add the extracted token as a secret. You can use this secret in your action workflows. The secret will be encrypted and stored safely. Unauthorised users will not be able to see it in logs when you use it in script.

We’ll name our secret NETLIFY_CRON_BUILD_HOOK:

List secrets in GitHub settings
List secrets in GitHub settings

To create our “nightly build” we add a GitHub workflow to your project by creating a file named .github/workflows/nightly-build.yml. Inside the workflow we use a scheduled event(cron job) to trigger our deploy:

name: Scheduled build
  - cron: '30 3 * * *'
    runs-on: ubuntu-latest
    - name: Trigger our build webhook on Netlify
      run: curl -s -X POST "${TOKEN}"
        TOKEN: ${{ secrets.NETLIFY_CRON_BUILD_HOOK }}

The cron job triggers a build step that triggers the Netlify build hook. A few notes:

  • We make our token available within the build step by reading the GitHub secret we defined earlier TOKEN: ${{ secrets.NETLIFY_CRON_BUILD_HOOK }}.
  • We append the token to our curl request${TOKEN}.
  • We use the -s flag to skip the default progress bar.
  • We use cron: '30 3 * * *' for our 5:30 AM deploys as we’re in The Netherlands (Central European Summer Time) while the GitHub workflows are scheduled in UTC.

Scheduled deploys in action

Github detects and enables workflows automatically. So after committing the workflow to master, our nightly builds are scheduled! We can see our nightly builds under the Actions tab of our GitHub repository and the deploy logs on Netlify:

Logs of our scheduled build under GitHub Actions tab
Logs of our scheduled build under GitHub Actions tab
Logs of our scheduled build in the Netlify Deploy logs
Logs of our scheduled build in the Netlify Deploy logs

That’s it. An updated version of our site is deployed nightly. You can see the scheduled Netlify deploy workflow in action here:

← All blog posts

Also in love with the web?

For us, that’s about technology and user experience. Fast, available for all, enjoyable to use. And fun to build. This is how our team bands together, adhering to the same values, to make sure we achieve a solid result for clients both large and small. Does that fit you?

Join our team