136 lines
4.4 KiB
Plaintext
Vendored
136 lines
4.4 KiB
Plaintext
Vendored
---
|
|
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.
|