Cloudflare Workers CI/CD
Status: ✅ Production Ready | Last Verified: 2025-01-27 GitHub Actions: v4 | GitLab CI: Latest | Wrangler: 4.50.0
Table of Contents
-
What Is Workers CI/CD?
-
New in 2025
-
Quick Start (10 Minutes)
-
Critical Rules
-
Core Concepts
-
Top 5 Use Cases
-
Best Practices
-
Top 7 Errors Prevented
-
When to Load References
What Is Workers CI/CD?
Automated testing and deployment of Cloudflare Workers using GitHub Actions or GitLab CI. Enables running tests on every commit, deploying to preview/staging/production environments automatically, managing secrets securely, and implementing deployment gates for safe releases.
Key capabilities: Automated testing, multi-environment deployments, preview URLs per PR, secrets management, deployment verification, automatic rollbacks.
New in 2025
GitHub Actions Updates (January 2025):
-
NEW: cloudflare/wrangler-action@v4 (improved caching, faster deployments)
-
IMPROVED: Secrets support with vars and secrets parameters
-
ADDED: Built-in preview environment cleanup
-
BREAKING: apiToken renamed to api-token (kebab-case)
Migration from v3:
❌ OLD (v3)
- uses: cloudflare/wrangler-action@3 with: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
✅ NEW (v4)
- uses: cloudflare/wrangler-action@v4 with: api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
Wrangler 4.50.0 (January 2025):
-
NEW: --dry-run flag for deployment validation
-
IMPROVED: Faster deployments with parallel uploads
-
ADDED: --keep-vars to preserve environment variables
Quick Start (10 Minutes)
GitHub Actions Setup
- Create Cloudflare API Token
Go to: https://dash.cloudflare.com/profile/api-tokens
Create token with permissions:
-
Account.Cloudflare Workers Scripts - Edit
-
Account.Cloudflare Pages - Edit (if using Pages)
- Add Secret to GitHub
Repository → Settings → Secrets → Actions → New repository secret:
-
Name: CLOUDFLARE_API_TOKEN
-
Value: [paste token]
- Create .github/workflows/deploy.yml
name: Deploy
on: push: branches: [main] pull_request: branches: [main]
jobs: deploy: runs-on: ubuntu-latest name: Deploy to Cloudflare Workers
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- run: bun install
- run: bun test
- name: Deploy
uses: cloudflare/wrangler-action@v4
with:
api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
command: deploy
4. Push and Verify
git add .github/workflows/deploy.yml git commit -m "Add CI/CD pipeline" git push
Check Actions tab on GitHub to see deployment progress.
Critical Rules
- Never Commit Secrets to Git
✅ CORRECT:
Use GitHub Secrets
api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
❌ WRONG:
❌ NEVER hardcode tokens
api-token: "abc123def456..."
Why: Exposed tokens allow anyone to deploy to your account.
- Always Run Tests Before Deploy
✅ CORRECT:
-
run: bun test # ✅ Tests run first
-
name: Deploy uses: cloudflare/wrangler-action@v4 with: api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
❌ WRONG:
❌ Skipping tests
- name: Deploy
uses: cloudflare/wrangler-action@v4
No tests!
Why: Broken code shouldn't reach production.
- Use Different Environments
✅ CORRECT:
Production (main branch)
- name: Deploy to Production if: github.ref == 'refs/heads/main' run: bunx wrangler deploy --env production
Staging (other branches)
- name: Deploy to Staging if: github.ref != 'refs/heads/main' run: bunx wrangler deploy --env staging
❌ WRONG:
❌ Always deploying to production
- run: bunx wrangler deploy
Why: Test changes in staging before production.
- Verify Deployment Success
✅ CORRECT:
-
name: Deploy id: deploy uses: cloudflare/wrangler-action@v4
-
name: Verify Deployment run: | curl -f https://your-worker.workers.dev/health || exit 1
❌ WRONG:
❌ No verification
- name: Deploy
uses: cloudflare/wrangler-action@v4
Assuming it worked...
Why: Deployments can fail silently (DNS issues, binding errors).
- Use Deployment Gates for Production
✅ CORRECT:
deploy-production: environment: name: production url: https://your-worker.workers.dev
Requires manual approval
❌ WRONG:
❌ Auto-deploy to production without review
deploy-production: runs-on: ubuntu-latest
Why: Human review catches issues automation misses.
Core Concepts
Multi-Environment Strategy
Recommended setup:
-
Production: main branch → production environment
-
Staging: Pull requests → staging environment
-
Preview: Each PR → unique preview URL
wrangler.jsonc:
{ "name": "my-worker", "main": "src/index.ts",
"env": { "production": { "name": "my-worker-production", "vars": { "ENVIRONMENT": "production" } }, "staging": { "name": "my-worker-staging", "vars": { "ENVIRONMENT": "staging" } } } }
Secrets Management
Types of configuration:
-
Public variables (wrangler.jsonc) - Non-sensitive config
-
Secrets (wrangler secret) - API keys, tokens
-
CI variables (GitHub Secrets) - Deployment credentials
Setting secrets:
Local development
wrangler secret put DATABASE_URL
CI/CD (via GitHub Actions)
bunx wrangler secret put DATABASE_URL --env production <<< "${{ secrets.DATABASE_URL }}"
Preview Deployments
Automatically deploy each PR to a unique URL for testing:
- name: Deploy Preview uses: cloudflare/wrangler-action@v4 with: api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} command: deploy --env preview-${{ github.event.number }}
Each PR gets URL like: my-worker-preview-42.workers.dev
Top 5 Use Cases
- Deploy on Push to Main
name: Deploy Production
on: push: branches: [main]
jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v2 - run: bun install - run: bun test - run: bun run build
- name: Deploy to Production
uses: cloudflare/wrangler-action@v4
with:
api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
command: deploy --env production
2. Preview Deployments for PRs
name: Preview
on: pull_request: branches: [main]
jobs: preview: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v2 - run: bun install - run: bun test
- name: Deploy Preview
id: deploy
uses: cloudflare/wrangler-action@v4
with:
api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
command: deploy --env preview-${{ github.event.number }}
- name: Comment PR
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '✅ Preview deployed to: https://my-worker-preview-${{ github.event.number }}.workers.dev'
})
3. Run Tests on Every Commit
name: Test
on: push: branches: ['**'] pull_request:
jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v2 - run: bun install - run: bun test --coverage
- name: Upload Coverage
uses: codecov/codecov-action@v4
with:
files: ./coverage/lcov.info
4. Deploy with Approval Gate
name: Deploy Production (Manual Approval)
on: push: branches: [main]
jobs: deploy: runs-on: ubuntu-latest environment: name: production url: https://my-worker.workers.dev # Requires manual approval in GitHub Settings
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install
- run: bun test
- name: Deploy
uses: cloudflare/wrangler-action@v4
with:
api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
command: deploy --env production
5. Staged Rollout (Canary)
name: Canary Deployment
on: workflow_dispatch: inputs: percentage: description: 'Traffic percentage to new version' required: true default: '10'
jobs: canary: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v2 - run: bun install
# Deploy to canary environment
- name: Deploy Canary
uses: cloudflare/wrangler-action@v4
with:
api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
command: deploy --env canary
# Configure traffic split via Cloudflare API
# (See references/deployment-strategies.md for full example)
Best Practices
✅ DO
Use semantic commit messages:
feat: add user authentication fix: resolve rate limiting issue chore: update dependencies
Run linting and type checking:
-
run: bun run lint
-
run: bun run type-check
-
run: bun test
Cache dependencies:
- uses: oven-sh/setup-bun@v2 with: bun-version: latest
Bun automatically caches dependencies
Deploy different branches to different environments:
-
name: Deploy run: | if [ "${{ github.ref }}" == "refs/heads/main" ]; then bunx wrangler deploy --env production else bunx wrangler deploy --env staging fi
Monitor deployments:
- name: Notify Slack if: failure() uses: slackapi/slack-github-action@v1 with: payload: | {"text": "Deployment failed: ${{ github.sha }}"}
❌ DON'T
-
Don't skip tests
-
Don't deploy without verification
-
Don't hardcode secrets
-
Don't deploy to production from feature branches
-
Don't ignore deployment failures
Top 7 Errors Prevented
- ❌ Error: A valid Cloudflare API token is required
Cause: Missing or invalid CLOUDFLARE_API_TOKEN secret.
Fix:
-
Create API token: https://dash.cloudflare.com/profile/api-tokens
-
Add to GitHub Secrets: Settings → Secrets → Actions
-
Use in workflow: api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
- ❌ Error: Not enough permissions to deploy
Cause: API token lacks required permissions.
Fix: Recreate token with:
-
Account.Cloudflare Workers Scripts - Edit
-
Account settings - Read
- ❌ Error: wrangler.toml not found
Cause: Missing wrangler configuration.
Fix: Ensure wrangler.jsonc exists in repository root.
- ❌ Deployment succeeds but worker doesn't work
Cause: Missing secrets or environment variables.
Fix: Set secrets in CI:
- name: Set Secrets run: | echo "${{ secrets.DATABASE_URL }}" | bunx wrangler secret put DATABASE_URL --env production
- ❌ Tests pass locally but fail in CI
Cause: Environment differences (Node version, missing dependencies).
Fix:
-
uses: oven-sh/setup-bun@v2 with: bun-version: latest # Lock version
-
run: bun install --frozen-lockfile # Use exact versions
- ❌ Preview deployments conflict
Cause: Multiple PRs deploying to same preview environment.
Fix: Use PR number in environment name:
command: deploy --env preview-${{ github.event.number }}
- ❌ Secrets exposed in logs
Cause: Echoing secrets in workflow.
Fix:
❌ WRONG
- run: echo "Token: ${{ secrets.API_TOKEN }}"
✅ CORRECT
- run: echo "Deploying..." # No secrets in output
When to Load References
Load reference files for detailed, specialized content:
Load references/github-actions.md when:
-
Setting up GitHub Actions from scratch
-
Configuring matrix builds (multiple Node versions)
-
Using GitHub environments and deployment protection
-
Implementing deployment gates and approvals
Load references/gitlab-ci.md when:
-
Setting up GitLab CI pipelines
-
Configuring GitLab environments
-
Using GitLab secret variables
-
Implementing review apps
Load references/deployment-strategies.md when:
-
Implementing blue-green deployments
-
Setting up canary releases
-
Configuring traffic splitting
-
Planning rollback procedures
Load references/secrets-management.md when:
-
Managing secrets across environments
-
Rotating API tokens
-
Using external secret providers (Vault, 1Password)
-
Implementing least-privilege access
Load templates/github-actions-full.yml for:
-
Complete production-ready GitHub Actions workflow
-
Multi-environment deployment example
-
All deployment gates configured
Load templates/gitlab-ci-full.yml for:
-
Complete GitLab CI pipeline
-
Multi-stage deployment
-
Review app configuration
Load templates/preview-deployment.yml for:
-
PR preview deployment setup
-
Automatic cleanup on PR close
-
Comment with preview URL
Load templates/rollback-workflow.yml for:
-
Manual rollback workflow
-
Deployment history tracking
-
Automated rollback on health check failure
Load scripts/verify-deployment.sh for:
-
Automated deployment verification
-
Health check implementation
-
Smoke tests after deployment
Related Cloudflare Plugins
For deployment testing, load:
-
cloudflare-workers-testing - Test Workers before deployment
-
cloudflare-manager - Manage deployments via Cloudflare API
This skill focuses on CI/CD automation for ALL Workers deployments regardless of bindings used.
Questions? Load references/secrets-management.md or use /workers-deploy command for guided deployment.