> cloudflare-workers-ci-cd
Complete CI/CD guide for Cloudflare Workers using GitHub Actions and GitLab CI. Use for automated testing, deployment pipelines, preview environments, secrets management, or encountering deployment failures, workflow errors, environment configuration issues.
curl "https://skillshub.wtf/secondsky/claude-skills/cloudflare-workers-ci-cd?format=md"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
varsandsecretsparameters - ADDED: Built-in preview environment cleanup
- BREAKING:
apiTokenrenamed toapi-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-runflag for deployment validation - IMPROVED: Faster deployments with parallel uploads
- ADDED:
--keep-varsto preserve environment variables
Quick Start (10 Minutes)
GitHub Actions Setup
1. 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)
2. Add Secret to GitHub
Repository → Settings → Secrets → Actions → New repository secret:
- Name:
CLOUDFLARE_API_TOKEN - Value: [paste token]
3. 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
1. 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.
2. 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.
3. 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.
4. 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).
5. 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:
mainbranch → 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
1. 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
1. ❌ 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 }}
2. ❌ Error: Not enough permissions to deploy
Cause: API token lacks required permissions.
Fix: Recreate token with:
- Account.Cloudflare Workers Scripts - Edit
- Account settings - Read
3. ❌ Error: wrangler.toml not found
Cause: Missing wrangler configuration.
Fix: Ensure wrangler.jsonc exists in repository root.
4. ❌ 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
5. ❌ 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
6. ❌ 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 }}
7. ❌ 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.
> related_skills --same-repo
> zustand-state-management
--- name: zustand-state-management description: Zustand state management for React with TypeScript. Use for global state, Redux/Context API migration, localStorage persistence, slices pattern, devtools, Next.js SSR, or encountering hydration errors, TypeScript inference issues, persist middleware problems, infinite render loops. Keywords: zustand, state management, React state, TypeScript state, persist middleware, devtools, slices pattern, global state, React hooks, create store, useBoundS
> zod
TypeScript-first schema validation and type inference. Use for validating API requests/responses, form data, env vars, configs, defining type-safe schemas with runtime validation, transforming data, generating JSON Schema for OpenAPI/AI, or encountering missing validation errors, type inference issues, validation error handling problems. Zero dependencies (2kb gzipped).
> xss-prevention
--- name: xss-prevention description: XSS attack prevention with input sanitization, output encoding, Content Security Policy. Use for user-generated content, rich text editors, web application security, or encountering stored XSS, reflected XSS, DOM manipulation, script injection errors. Keywords: sanitization, HTML-encoding, DOMPurify, CSP, Content-Security-Policy, rich-text-editor, user-input, escaping, innerHTML, DOM-manipulation, stored-XSS, reflected-XSS, input-validation, output-encodi
> wordpress-plugin-core
--- name: wordpress-plugin-core description: WordPress plugin development with hooks, security, REST API, custom post types. Use for plugin creation, $wpdb queries, Settings API, or encountering SQL injection, XSS, CSRF, nonce errors. Keywords: wordpress plugin development, wordpress security, wordpress hooks, wordpress filters, wordpress database, wpdb prepare, sanitize_text_field, esc_html, wp_nonce, custom post type, register_post_type, settings api, rest api, admin-ajax, wordpress sql inj