---
title: "Deploying to Cloudflare Pages with GitHub Actions"
description: "A simple CI/CD workflow for deploying static site to Cloudflare Pages using GitHub Actions"
date: 2026-03-26
tags: ['CI/CD', 'Gitea', 'Cloudflare Pages', 'DevOps']
authors:
  - "Patrick Jaroszewski"
slug: "gitea-cloudflare-deployment"
---

# Deploying to Cloudflare Pages with Gitea Actions

Cloudflare Pages has built-in integrations with GitHub and GitLab, but if you're self-hosting with Gitea or want more control over your deployment pipeline, you'll need to set things up manually. Fortunately, Gitea Actions is compatible with GitHub Actions, which means Cloudflare's official Wrangler action works out of the box.

This post walks through the workflow I use to deploy this site and others I host for clients.

---

## The Basic Workflow

Here's a minimal Gitea Actions workflow that builds an Astro site and deploys it to Cloudflare Pages:

```yaml title=".gitea/workflows/deploy.yaml"
name: Generate a build and push to Cloudflare Pages

on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    name: Build and Deploy to Cloudflare Pages

    steps:
      - name: git-checkout
        uses: actions/checkout@v5

      - name: pnpm-setup
        uses: pnpm/action-setup@v4
        with:
          version: 10

      - name: Install Node.js
        uses: actions/setup-node@v6
        with:
          node-version: 24
          cache: pnpm

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Build
        run: pnpm build

      - name: Deploy
        uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          command: pages deploy ./dist --project-name="your-project-name"
```

The workflow triggers on pushes to `main`, installs dependencies, builds the site, and deploys using Cloudflare's Wrangler action. Every push to main goes straight to production.

---

## Using a Separate Production Branch

Deploying on every push to `main` works well for small projects, but you might want more control over when deployments happen. Some reasons to consider a separate deployment branch:

- **Batching changes** — deploy multiple commits together rather than one at a time
- **Review before release** — test locally or stage changes before they go live
- **Avoiding accidental deployments** — a typo fix shouldn't necessarily trigger a full deploy
- **Coordinating releases** — deploy on your schedule, not whenever you happen to push

The setup is simple: change the workflow trigger from `main` to `prod`:

```yaml
on:
  push:
    branches:
      - prod
```

Then add a script to promote `main` to `prod` when you're ready:

```json title="package.json"
{
  "scripts": {
    "deploy": "git push origin main:prod"
  }
}
```

Now you develop freely on `main`, and run `pnpm deploy` when you want to go live.

---

## The Cloudflare Branch Gotcha

If you use a `prod` branch, there's a catch: Cloudflare Pages uses the **branch name** to determine the deployment environment.

- `main` or `master` → production deployment (your primary URL)
- Any other branch → preview deployment (e.g., `prod.your-project.pages.dev`)

If you deploy from a branch called `prod`, Cloudflare treats it as a preview deployment, not production. Your site ends up at a subdomain instead of your main URL.

**Two solutions:**

1. **Change the production branch in Cloudflare** — Go to your Pages project → Settings → Builds & deployments → Production branch, and change it from `main` to `prod`.

2. **Use the `--branch` flag** — Force Wrangler to treat the deployment as production:

```yaml
command: pages deploy ./dist --project-name="your-project" --branch=main
```

Option 1 is cleaner because it aligns Cloudflare's understanding with your actual workflow. Option 2 is a quick workaround if you can't change the settings.

---

## Required Secrets

You'll need to configure two secrets in your Gitea repository (Settings → Actions → Secrets):

- `CLOUDFLARE_API_TOKEN` — Create an API token in Cloudflare with "Cloudflare Pages: Edit" permissions
- `CLOUDFLARE_ACCOUNT_ID` — Found in your Cloudflare dashboard URL or account settings

---

## Wrapping Up

Whether you deploy on every push to `main` or use a separate `prod` branch, the core setup is the same: Gitea Actions builds your site and Wrangler pushes it to Cloudflare.
