Using Cloudflare Workers and GitHub Actions to Deploy Statically Generated Sites

Using Cloudflare Workers and GitHub Actions to Deploy Statically Generated Sites

Deploying performant websites at massive scale has never been easier (or cheaper)

All hail the JAMStack

Sites built on the JAMStack are all the rage these days; they’ve become a popular alternative to full-stack frameworks and server-side rendered web applications. They’re lightweight, performant, and are extremely easy to deploy on platforms like Vercel or Netlify. With that in mind, let’s take a look at how we can step up our deployment game and reduce load times to a minimum.

Netlify is great…. but?

Enter Cloudflare Workers. Workers are a serverless platform that allow you to deploy code in various languages like JavaScript, Rust, Python, Kotlin etc. A huge advantage that they possess over other serverless platforms such as AWS Lambda is that workers automatically deploy your code across the globe, thanks to Cloudflare’s massive CDN.

Serverless code is great, but what about content?

Workers also recently released a KV store, that can be used to store static content such as CSS or JS chunks. This makes workers ideal for deploying a performant site at massive scale.

That’s great! How do we use one?

Whoa there, hold yer horses. Let’s understand what a worker does exactly before jumping into it.

A worker is a piece of code that’s executed when a particular route on a website proxied by Cloudflare is accessed, before the request reaches Cloudflare’s cache.

The following is a super simple worker, which just responds with a Hello World!.

Wrangling your workers with wrangler

Cloudflare has a great tool to configure workers called wrangler. Install it globally using:

# With NPM
npm i -g @cloudflare/wrangler

# With yarn
yarn global add @cloudflare/wrangler

Or just add it to your devDependencies.

Now we need to authorize wrangler to create and edit workers. Run wrangler login to automatically add an API key to your local wrangler config file.

Configuring a domain

Every site needs a domain, and our site is no different! First, you need your domain to be added to the Cloudflare dashboard. I already have [sphericalkat.dev](https://sphericalkat.dev) added to mine.

In the DNS section, add a record of type A, with whatever subdomain you wish that points to any unreachable IP address like 192.2.0.1. This isn’t strictly necessary, but it’s recommended since our worker will intercept all requests and the IP will never resolve.

DNS record

Creating a statically generated site

Any static site generator should work for this step. I’m personally using Next.js, but you can use whichever one you like.

Add @cloudflare/wranglerto your devDependencies and set up a wrangler project locally. This will be useful for deploying the site from GitHub actions.

❯ yarn add -D @cloudflare/wrangler
❯ wrangler init --site site-worker

⬇️ Installing cargo-generate...
🔧 Creating project called `workers-site`...
✨ Done! New project created /Users/sphericalkat/WebstormProjects/sphericalkat.dev/workers-site
✨ Succesfully scaffolded workers site
✨ Succesfully created a `wrangler.toml`

We’ve got a bunch of new files. Let’s take a look:

  • workers-site directory: Contains our worker inside an index.js file, and the necessary dependencies
  • wrangler.toml file: worker deployment configuration

The above are the contents of the index.js worker. We don’t need to modify this for now, but we’ll make some changes later to get page routing working.

However, wrangler.toml needs some updates, which are highlighted below in bold.

name = "site-worker"
type = "webpack"
account_id = ""
workers_dev = true

[site]
bucket = "./out"
entry-point = "workers-site"

We can now run wrangler publish to deploy our site to a staging environment.

❯ wrangler publish

✨ Built successfully, built project size is 13 KiB.
🌀 Created namespace for Workers Site "__site-worker-workers_sites_assets"
✨ Success
🌀 Uploading site files
✨ Successfully published your script to
https://site-worker.amolele.workers.dev

Staging site deployment

Success! As we can see, the site is now live (and loads super fast) at the URL that wrangler gave us. However you might notice that page routing is broken on reloads. Let’s fix that by modifying the index.js inside workers-site.

This modified worker file appends a .html extension to the path internally if routing fails, which allows our page routes to be loaded successfully.

Deploying to production

We don’t always want our site hosted at the temporary URL that Cloudflare gave us, and we most definitely don’t want to publish our site manually every time we make some changes. Let’s remedy that.

Enter GitHub actions

GitHub actions allow us to run workflows on any GitHub actions. We’ll be using an action triggered by a push to deploy our site. Let’s create a workflow config file at .github/workflows/workers-deploy.yml.

Github actions workflow config

You might have noticed the workflow file using some secrets. Well done! These secrets are necessary since we don’t want to expose our API token and account/zone ids to the entire world. Add them from your repo settings like below.

GitHub actions secrets

We also need some modifications to the wrangler.toml for production deployments. The modifications are marked in bold.

type = "webpack"
workers_dev = false

[site]
bucket = "./out"
entry-point = "workers-site"

[env.production]
name = "site-worker-production"
route = "sphericalkat.dev/*"

The production options point a route to our worker. This is necessary for accessing the site using our custom domain.

Let’s see if the deploy worked!

Github actions output

Looks like it did. And sure enough, the site is live at sphericalkat.dev

Production deployment

Conclusions

  • Sites require a CDN to be performant. Cloudflare is a known and proven solution.
  • Cloudflare Workers and Cloudflare Workers KV allows us to directly deploy statically generated sites to the edge.
  • We can use CI/CD pipelines to automate deployments.

That’s all folks, stay safe and have fun!