Cloudflare Pages
Deploy static sites (React, Vue, Astro, etc.) to Cloudflare Pages with automatic preview deployments and production branches.
Prerequisites
- Node.js and npm (or pnpm) installed
- Wrangler CLI available via npx
- A Cloudflare account with a Pages project already created
- A Cloudflare API token
Create the Pages Project
You must create the Cloudflare Pages project before deploying. The project name must exactly match the name field in your pilum.yaml.
Option A: Cloudflare Dashboard
- Go to Workers & Pages in the sidebar
- Click Create → Pages
- Choose Direct Upload (not “Connect to Git” — Pilum handles the deploy)
- Name the project to match your
pilum.yamlnamefield - Click Create — you don’t need to upload anything
Option B: Wrangler CLI
npx wrangler pages project create my-siteAPI Token
# Create an API token:# Cloudflare Dashboard → My Profile → API Tokens → Create Token# Use the "Edit Cloudflare Workers" template or create custom with Pages permissions
# Verify it worksnpx wrangler --versionService Configuration
name: my-siteprovider: cloudflare
cloudflare: account_id: "abc123def456" token_env: "CLOUDFLARE_API_TOKEN" production_branch: "main" package_manager: "npm"
build: cmd: "npm run build" output_dir: "dist"Required Fields
| Field | Description |
|---|---|
name | Project name (must match your Cloudflare Pages project name) |
cloudflare.account_id | Cloudflare account ID |
Optional Fields
Cloudflare Configuration
| Field | Type | Default | Description |
|---|---|---|---|
cloudflare.token_env | string | CLOUDFLARE_API_TOKEN | Environment variable name containing the API token |
cloudflare.production_branch | string | main | Branch name that triggers production deployment |
cloudflare.package_manager | string | npm | Package manager (npm or pnpm) |
Build Configuration
| Field | Type | Default | Description |
|---|---|---|---|
build.cmd | string | npm run build | Build command to run |
build.output_dir | string | dist | Directory containing built static files |
Deployment Steps
The Cloudflare Pages recipe executes:
- install dependencies —
npm ciorpnpm install --frozen-lockfile - build site — Run build command (skipped if no
build.cmdconfigured) - deploy to pages — Deploy via
npx wrangler pages deploy
Production vs Preview Deploys
Pilum uses the --tag flag to determine deployment type:
- Production: When tag matches
production_branch(default:main) or islatest - Preview: Any other tag value creates a preview deployment
# Production deploypilum deploy --tag=main
# Preview deploy (e.g., from a feature branch)pilum deploy --tag=feature-loginPreview deployments get a unique URL like https://<branch>.my-site.pages.dev.
Package Manager
npm (default)
cloudflare: package_manager: "npm"Installs with npm ci for reproducible builds from the lockfile.
pnpm
cloudflare: package_manager: "pnpm"Installs with pnpm install --frozen-lockfile.
pnpm Monorepo
For pnpm workspaces where the site is one package in a monorepo, set the build command to filter from the workspace root:
name: my-siteprovider: cloudflare
cloudflare: account_id: "abc123def456" package_manager: "pnpm"
build: cmd: "pnpm --filter @scope/my-site... build" output_dir: "apps/my-site/dist"The ... suffix builds the package AND all its workspace dependencies in order. The output_dir should point to the specific package’s dist folder relative to the monorepo root.
Build Step
The build step runs your site’s build command via shell:
build: cmd: "npm run build" output_dir: "dist"If no build.cmd is set, the step is skipped automatically. The output_dir tells wrangler which directory to deploy.
Common output directories by framework:
| Framework | Output Dir |
|---|---|
| Vite / React / Vue | dist |
| Next.js (static) | out |
| Astro | dist |
| Create React App | build |
Generated Deploy Script
Pilum generates a deploy script that:
- Validates the API token environment variable is set
- Exports
CLOUDFLARE_API_TOKENandCLOUDFLARE_ACCOUNT_ID - Deploys via
npx wrangler pages deploy
if [ -z "$CLOUDFLARE_API_TOKEN" ]; then echo "Error: CLOUDFLARE_API_TOKEN environment variable is not set" exit 1fiexport CLOUDFLARE_API_TOKEN="$CLOUDFLARE_API_TOKEN"export CLOUDFLARE_ACCOUNT_ID="abc123def456"npx wrangler pages deploy dist --project-name=my-site --branch=mainExample Commands
# Full deploy pipeline (build + deploy)pilum deploy --tag=main
# Build only (no deploy)pilum build
# Preview commandspilum dry-run --tag=main
# Deploy only (assumes already built)pilum deploy --only-tags=deploy --tag=main
# Preview deployment from feature branchpilum deploy --tag=feature-authCI/CD Integration
GitHub Actions (pilum-action)
The recommended approach uses the official pilum-action:
name: Deployon: push: branches: [main] workflow_call: inputs: tag: required: true type: string
jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20'
- name: Deploy with Pilum uses: SID-Technologies/pilum-action@v1 with: command: deploy services: my-site tag: ${{ inputs.tag || github.ref_name }} env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}GitHub Actions (manual install)
Alternatively, install Pilum directly:
name: Deployon: push: branches: [main]
jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20'
- name: Install Pilum run: | curl -L https://github.com/SID-Technologies/Pilum/releases/latest/download/pilum-linux-amd64.tar.gz | tar xz sudo mv pilum /usr/local/bin/
- name: Deploy env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} run: pilum deploy --tag=${{ github.ref_name }}Both approaches deploy to production on pushes to main and can create preview deployments for feature branches.
Troubleshooting
Authentication Failed
Error: CLOUDFLARE_API_TOKEN environment variable is not setEnsure the token is exported:
export CLOUDFLARE_API_TOKEN="your-token-here"Project Not Found
Error: A request to the Cloudflare API failed. Could not find projectThe name in your pilum.yaml must exactly match your Cloudflare Pages project name. Create the project first in the Cloudflare dashboard or via:
npx wrangler pages project create my-siteWrong Output Directory
No files found in dist/Check that build.output_dir matches where your framework outputs built files:
build: cmd: "npm run build" output_dir: "build" # e.g., for Create React AppWrangler Not Found
npx: command not found: wranglerEnsure Node.js and npm are installed. Wrangler is invoked via npx so it doesn’t need a global install.
Next Steps
- Service Configuration — Full reference
- CLI Commands — All commands
- Troubleshooting — Common issues