Table of Contents 8 Sections
Overview
This guide sets up a GitHub Actions workflow that automatically builds, tests, and deploys a static site to Cloudflare Pages every time you push to your main branch — no manual build-and-upload steps. Every technical term used along the way (workflow, runner, secret, and so on) is explained in the Glossary section below before it's used.
Budget about 15–20 minutes: a few minutes to add the workflow file, plus however long it takes you to generate a Cloudflare API token and confirm your project's build/test commands actually work locally. There's no ongoing cost beyond what you already pay for GitHub and Cloudflare Pages (both are free for typical personal projects; GitHub Actions minutes and Cloudflare Pages both have generous free tiers). This guide has been written from technical documentation and general best practice, but — like every guide currently on Workflow Vault — it is UNVERIFIED: it has not yet been run end-to-end and confirmed working by Workflow Vault's own testing. Read each step before applying it, and treat this as a well-researched starting point rather than a guarantee.
Glossary
- GitHub Actions
- GitHub Actions is GitHub's built-in automation system. You describe a series of steps in a file, and GitHub runs them automatically whenever something happens in your repository — for example, every time you push code.
- Workflow
- A workflow is one automation recipe, written as a YAML file inside a .github/workflows folder in your repository. This guide creates a single workflow file that handles building, testing, and deploying.
- YAML
- YAML is a plain-text format for writing structured configuration, using indentation instead of brackets or braces. GitHub Actions workflows are written in YAML, so indentation matters — copy the examples in this guide exactly.
- Trigger
- A trigger is the event that causes a workflow to run. This guide's workflow is triggered by a push to the main branch, meaning it starts automatically every time new code lands there.
- Job
- A job is a group of steps in a workflow that all run on the same machine, one after another. This guide uses a single job called deploy that does everything: install, build, test, and publish.
- Runner
- A runner is the actual computer that executes your workflow's steps. This guide uses a GitHub-hosted runner (ubuntu-latest), meaning GitHub provisions a fresh Linux machine for each run and throws it away afterward — you don't manage or pay for a server yourself.
- Action
- An action is a reusable, packaged step that someone else has already written, referenced with uses: in a workflow file — for example, actions/checkout, which downloads your repository's code onto the runner so later steps can work with it.
- Secret
- A secret is a piece of sensitive configuration (like an API token) stored encrypted in your repository's settings rather than written directly into the workflow file. Workflows can read secrets at runtime, but they're never shown in logs or visible to anyone browsing the repository.
- Cloudflare Pages
- Cloudflare Pages is a hosting service for static sites and front-end apps. This guide deploys a project's built output to Cloudflare Pages, which serves it from Cloudflare's global network.
- Build output / build command
- Most front-end projects have a build command (commonly npm run build) that compiles source files into a folder of plain HTML, CSS, and JavaScript — the build output — that's actually safe to publish to the web. This guide assumes that folder is named dist, which is the default for many project types, but yours may use a different name.
- npm ci
- npm ci installs a project's dependencies from its package-lock.json file exactly as recorded, rather than resolving newer versions the way a plain npm install might. It's the standard, more reliable choice for automated environments like CI.
- CI/CD
- CI/CD stands for Continuous Integration / Continuous Deployment — the general practice of automatically building, testing, and shipping code on every change, instead of doing those steps by hand. This entire guide is one concrete example of a CI/CD pipeline.
Prerequisites
- [You'll need this already] A GitHub repository containing a Node.js project with working build and test scripts defined in package.json (for example npm run build and npm test) — confirm both run successfully on your own machine before automating them.
- [You'll need this already] A Cloudflare Pages project already created and connected to this repository, or at minimum a Cloudflare account where you can create one. This guide does not cover creating the Cloudflare Pages project itself, only automating deploys to it.
- [You'll need this already] A Cloudflare API token with Pages edit permission, generated from your Cloudflare dashboard under My Profile, then API Tokens.
- [You'll need this already] Comfort editing a plain-text YAML file and pushing commits to GitHub. You do not need prior experience with GitHub Actions specifically — every term used is explained in the Glossary above.
- [Optional, not required] If your project doesn't have a test script yet, either add a minimal one before following this guide, or skip the 'Run tests before deploying' step below — running npm test with no test script configured will make the workflow fail.
Where to Run This Automation
Where should you run this automation?
Don't have 24/7 hardware at home? Choose the setup that fits your budget and technical comfort.
Best for Freelancers, Agencies, and 24/7 Client Pipelines
If you don't have a dedicated server running at home, a Virtual Private Server (VPS) gives you a dedicated Linux server in the cloud that stays online 24/7 even when your personal laptop is closed.
- Cost: ~$4 to $6 / month (predictable flat fee, no per-execution surprises).
- Providers: Hetzner Cloud, DigitalOcean, Linode / Akamai, Railway, or OVH.
- Key advantage: You get a permanent public IP address and free SSL certificates, which are essential for receiving incoming Stripe, Airtable, or GitHub webhooks reliably.
curl -fsSL https://get.docker.com | sh && sudo usermod -aG docker $USER Best for Hobbyists, Testing & Zero-Cost Experimentation
You can run this automation completely free on your everyday computer (Mac, Windows, or Linux) or on low-cost home hardware like a Raspberry Pi 4/5 or repurposed mini PC.
- Cost: $0 / month (100% free).
- Software needed: Docker Desktop (free for personal use) or native Node.js / Python.
- Consideration: Automations will only execute while your computer is powered on and awake. To receive incoming webhooks locally, use tools like
ngrokorCloudflare Tunnelsfor secure local tunneling.
Best for Zero-Maintenance 1-Click Operations
If you don't want to manage Docker containers or terminal commands, you can run automations directly on managed serverless platforms or official SaaS plans:
- Cloudflare Workers: Free tier includes 100,000 requests per day with 0ms cold starts.
- n8n Cloud / Make / Zapier: Fully managed hosted platforms with automated backups and visual scenario builders.
- Railway / Render: 1-click Git container deploys with auto-healing and managed PostgreSQL databases.
Create the workflow file
In your repository, create a new file at .github/workflows/deploy.yml (GitHub automatically treats anything in that folder as a workflow). Paste in the starting configuration below: it names the workflow Deploy, sets it to trigger on every push to the main branch, and defines one job called deploy that runs on a fresh GitHub-hosted Linux machine. The first step inside that job checks out your repository's code so later steps have something to work with.
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4 Every workflow needs to declare what should trigger it and where its steps should run before it can do anything else. Triggering on pushes to main specifically — rather than every branch — means this pipeline only fires for code that's already been merged, not for work-in-progress branches.
After committing this file and pushing it to GitHub, open the Actions tab in your repository. You should see a new workflow run start automatically, named Deploy, though it will currently do nothing beyond checking out the code since no further steps exist yet.
Install and build
Add the steps below underneath the checkout step in the same file. They install Node.js on the runner, cache npm's downloads so future runs are faster, install your project's exact dependencies with npm ci, and then run your project's build command.
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run build The runner starts as a bare machine with nothing installed — it needs Node.js added explicitly before npm ci or npm run build will work. Caching dependencies (cache: npm) doesn't change what gets installed, only how long it takes on repeat runs.
Push this change and check the Actions tab again. The workflow run should now show green checkmarks next to a 'Set up Node' step and an 'npm ci' step, followed by your build command's own output — the same output you'd see running npm run build locally.
Run tests before deploying
Add a single line under the build steps to run your test suite. If this step fails, every step after it is skipped automatically — including the deploy step you'll add next — so a broken build never reaches production.
- run: npm test This is the entire point of putting tests in the pipeline instead of running them manually and remembering to deploy afterward: a failing test physically blocks the deploy step from running at all, rather than relying on you to notice and stop yourself.
Push a change that would normally fail your tests (or check a previous run) and confirm the workflow stops at this step with a red X, and that no deploy step appears to have run afterward. Then confirm a normal, passing push proceeds past this step with a green checkmark.
Add the secrets
Before wiring up the deploy step, store your Cloudflare credentials as repository secrets so they're never written in plain text anywhere in the workflow file. In your repository on GitHub, go to Settings, then Secrets and variables, then Actions, and add two new repository secrets: CF_API_TOKEN (the Cloudflare API token from the Prerequisites section) and CF_ACCOUNT_ID (found on the right-hand sidebar of your Cloudflare dashboard's overview page).
The upcoming deploy step needs to authenticate with Cloudflare, but pasting a real API token directly into a workflow file would expose it to anyone who can read your repository. Secrets are encrypted and only decrypted at runtime, inside the workflow run itself.
After saving both secrets, the Actions secrets page should list CF_API_TOKEN and CF_ACCOUNT_ID under 'Repository secrets,' each showing only its name and last-updated date — GitHub never displays a secret's actual value again once it's saved.
Deploy to Cloudflare Pages
Add the final step below, using Cloudflare's official pages-action to publish your build output. Replace my-site with your actual Cloudflare Pages project name, and replace dist with your build command's real output folder if it's named differently (check your project's build tool documentation if you're not sure).
- uses: cloudflare/pages-action@v1
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}
projectName: my-site
directory: dist This step is what actually ships your site — everything before it was preparation. Cloudflare Pages performs an atomic swap when it receives the new build, meaning visitors are switched over to the new version all at once rather than seeing a half-updated site mid-deploy.
After this step completes with a green checkmark, visit your Cloudflare Pages project's URL. It should reflect whatever change you just pushed. The Cloudflare Pages dashboard should also show a new deployment matching the commit you pushed.
Workflow architecture
A push to main triggers the workflow. A GitHub-hosted runner checks out the code, installs dependencies, builds the project, and runs the test suite in sequential steps — if any step fails, every step after it is skipped, so a broken build never reaches the deploy step. Only after tests pass does Cloudflare's official action authenticate with a repository secret and hand off the built directory, which Cloudflare Pages swaps into production atomically.
Final result
A push to main automatically builds, tests, and deploys your site to Cloudflare Pages with no manual steps, typically finishing within a couple of minutes depending on your project's size. As noted above, this guide is unverified by Workflow Vault — treat the outcome as expected-but-not-yet-confirmed until you've run it yourself.
Troubleshooting
The 'Run tests before deploying' step fails immediately with something like 'missing script: test'
Your package.json doesn't define a test script, so npm test has nothing to run.
Add a test script to package.json (even a placeholder that just exits successfully, if you don't have real tests yet), or remove the 'npm test' step from the workflow entirely if you're not ready to test in CI.
Deploy step fails with an authentication error
The CF_API_TOKEN secret is missing, misspelled, expired, or was generated without Pages edit permission.
Generate a new Cloudflare API token with Pages edit permission and update the CF_API_TOKEN repository secret, double-checking the secret's name matches exactly what the workflow file references.
Build succeeds locally but fails in the 'npm ci' step in CI
package-lock.json is out of sync with package.json, and npm ci enforces an exact match between them rather than resolving new versions like npm install would.
Run npm install locally to refresh the lockfile, commit the updated package-lock.json, and push again.
The deploy step succeeds but the live site doesn't show the new content
The directory input in the deploy step points at the wrong output folder, so Cloudflare is publishing an empty or stale folder instead of your actual build.
Confirm your build command's real output folder name (check your build tool's documentation or run the build command locally and see what folder appears) and make sure it matches the directory value in the pages-action step exactly.