CI/CD — GitHub Actions Integration
By the end of this module you will be able to build a complete GitHub Actions CI/CD pipeline that builds your Docusaurus site, runs tests, and deploys to Cloudflare Pages — giving you more control than the native Git integration.
When to Use GitHub Actions vs Native Git Integration
| Scenario | Recommendation |
|---|---|
| Simple static site, no pre-deploy tests | ✅ Use native Git integration |
| Need to run tests before deploy | ✅ Use GitHub Actions |
| Need to trigger deploy from scripts/CI | ✅ Use GitHub Actions + Wrangler |
| Monorepo with conditional deploys | ✅ Use GitHub Actions |
| Need deployment status in Slack/Teams | ✅ Use GitHub Actions |
| Custom build environment not supported by Pages | ✅ Use GitHub Actions |
You can disable the native Git integration and use only GitHub Actions, OR keep both (native integration as fallback + GitHub Actions for test-gated deploys). The author's tested approach uses GitHub Actions for control while keeping native integration disabled.
Method 1: Wrangler Action (Official)
Cloudflare provides an official GitHub Action: cloudflare/wrangler-action.
Basic Workflow
name: Deploy to Cloudflare Pages
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read
deployments: write # Required for GitHub deployment status
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build Docusaurus
run: npm run build
- name: Deploy to Cloudflare Pages
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy build --project-name my-docs
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
The gitHubToken field enables GitHub deployment status comments on PRs.
Full Production Workflow (With Tests)
name: Test & Deploy Docusaurus
on:
push:
branches: [main, staging]
pull_request:
branches: [main]
env:
NODE_VERSION: '20'
jobs:
# ─── Job 1: Lint & Build ──────────────────────────────
build:
name: Build
runs-on: ubuntu-latest
outputs:
# Pass build status to deploy job
build_success: ${{ steps.build.outcome }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Check for broken links (optional)
run: npx docusaurus build --no-minify 2>&1 | grep -i "broken\|error" || true
- name: Build
id: build
run: npm run build
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: docusaurus-build
path: build/
retention-days: 1
# ─── Job 2: Deploy ────────────────────────────────────
deploy:
name: Deploy
runs-on: ubuntu-latest
needs: build
permissions:
contents: read
deployments: write
steps:
- uses: actions/checkout@v4
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: docusaurus-build
path: build/
- name: Deploy to Cloudflare Pages
id: deploy
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: >
pages deploy build
--project-name my-docs
--branch ${{ github.ref_name }}
--commit-dirty=true
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
- name: Show deployment URL
run: echo "Deployed to ${{ steps.deploy.outputs.deployment-url }}"
# ─── Job 3: Notify ────────────────────────────────────
notify:
name: Notify
runs-on: ubuntu-latest
needs: [build, deploy]
if: always() && github.ref == 'refs/heads/main'
steps:
- name: Notify on success
if: needs.deploy.result == 'success'
run: |
echo "✅ Deployment to production succeeded"
# Add Slack/Discord webhook here
- name: Notify on failure
if: needs.deploy.result == 'failure'
run: |
echo "❌ Deployment failed! Check GitHub Actions logs."
Method 2: Direct Wrangler CLI in Steps
For more control without the Action:
name: Manual Deploy via Wrangler CLI
on:
workflow_dispatch: # Manual trigger only
inputs:
environment:
description: 'Deploy environment'
required: true
default: 'preview'
type: choice
options:
- preview
- production
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run build
- name: Install Wrangler
run: npm install -g wrangler
- name: Deploy (Preview)
if: inputs.environment == 'preview'
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
run: |
wrangler pages deploy build \
--project-name my-docs \
--branch preview-manual
- name: Deploy (Production)
if: inputs.environment == 'production'
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
run: |
wrangler pages deploy build \
--project-name my-docs \
--branch main
Setting Up GitHub Secrets
Add these secrets to your GitHub repository:
- Go to Settings → Secrets and variables → Actions
- Click New repository secret
| Secret Name | Value | How to Get |
|---|---|---|
CLOUDFLARE_API_TOKEN | Your CF API token | dash.cloudflare.com/profile/api-tokens |
CLOUDFLARE_ACCOUNT_ID | Your CF Account ID | wrangler whoami or CF dashboard sidebar |
API Token Permissions Required:
Account > Cloudflare Pages > Edit
Disabling Native Git Integration
When using GitHub Actions for deployments, disable the native integration to avoid double builds:
- Go to your Pages project → Settings → Builds & Deployments
- Click Pause deployments
- Now only your GitHub Actions pipeline triggers builds
Or keep native integration active — it will trigger builds from GitHub pushes, while your Actions workflow also deploys (both are valid, just costs 2 build slots per push).
Caching Dependencies
Speed up CI builds by caching node_modules:
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm' # Automatically caches ~/.npm based on package-lock.json
# This reduces npm install from ~60s to ~5s on cache hit
For Yarn or pnpm:
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'yarn' # or 'pnpm'
Deployment Status Outputs
The cloudflare/wrangler-action provides these outputs:
- name: Deploy
id: deploy
uses: cloudflare/wrangler-action@v3
with:
# ...
- name: Use deployment outputs
run: |
echo "URL: ${{ steps.deploy.outputs.deployment-url }}"
echo "ID: ${{ steps.deploy.outputs.deployment-id }}"
echo "Alias: ${{ steps.deploy.outputs.pages-deployment-alias-url }}"
Scheduled Builds (Cron)
Trigger builds on a schedule — useful for:
- Rebuilding docs that pull from external APIs
- Refreshing timestamps
name: Scheduled Rebuild
on:
schedule:
- cron: '0 6 * * *' # Daily at 6:00 AM UTC
workflow_dispatch: # Also allow manual trigger
jobs:
rebuild:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run build
- uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy build --project-name my-docs
Multi-Environment Deployment Matrix
name: Multi-Environment Deploy
on:
push:
branches: [main, staging, develop]
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read
deployments: write
strategy:
matrix:
include:
- branch: main
environment: production
pages_branch: main
- branch: staging
environment: staging
pages_branch: staging
- branch: develop
environment: develop
pages_branch: develop
# Only run for the branch that was pushed
if: github.ref_name == matrix.branch
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run build
env:
NODE_ENV: ${{ matrix.environment }}
- uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy build --project-name my-docs --branch ${{ matrix.pages_branch }}
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
Key Takeaways
- Use the official
cloudflare/wrangler-action@v3for the simplest GitHub Actions integration. - Store
CLOUDFLARE_API_TOKENandCLOUDFLARE_ACCOUNT_IDas GitHub repository secrets. - Use artifact upload/download between jobs to avoid rebuilding.
- Add
gitHubToken: ${{ secrets.GITHUB_TOKEN }}to get PR deployment status comments. - Disable native Pages Git integration if you want to control builds exclusively through GitHub Actions.
- Use
workflow_dispatchfor manual production deployments with human approval.
What's Next
- Continue to Operational Runbook & Troubleshooting for production-readiness, incident response, and common issue resolution.