Deploying a Rust/Axum App to Cloudflare Pages
Mizushi is a knowledge base engine built with Rust + Axum + Askama — it renders Markdown articles into HTML server-side. The goal was to host it on Cloudflare Pages (free tier).
The Problem
Cloudflare Pages' build environment does not have Rust or Cargo installed. Running cargo run in the build command fails immediately:
/bin/sh: 1: cargo: not found
Pages supports Node.js, Python, Go, Ruby, PHP — but not Rust.
Approach: Static Export + GitHub Actions
Instead of running the server dynamically, the solution is to pre-render all pages to static HTML during CI, then deploy the static output to Pages.
Step 1: export CLI Subcommand
Added an Export variant to the CLI enum:
pub enum Command {
Serve { port, content, static_dir },
Export { content, static_dir, output },
}
It loads all Markdown content via the existing load_content(), renders every page (index, articles, tags, search index) using Askama templates, and writes them to ./dist/. Static assets (CSS, JS) are copied as-is.
The output structure:
dist/
index.html
article/{slug}.html
tag/{tag}.html
search-index.json
css/style.css
js/app.js
Cloudflare Pages automatically serves article/slug for article/slug.html (extensionless HTML support).
Step 2: GitHub Actions Workflow
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions-rust-lang/setup-rust-toolchain@v1
- run: cargo run -- export --output dist
- uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy dist --project-name=mizushi
Step 3: API Token Pitfalls
The wrangler action authenticates via CLOUDFLARE_API_TOKEN. Two issues encountered:
- Token scope: The token needs Account-level permissions (not Zone-level). Required:
Account > Cloudflare Pages > EditAccount > Workers Scripts > Edit
- Account ID: Even with a valid token, the
/membershipsendpoint may fail. PassaccountIdexplicitly to skip membership discovery. Get it from the Dashboard URL:https://dash.cloudflare.com/<32-hex-id>.
Both values go into GitHub Secrets (not Variables).
Step 4: Creating the Pages Project
The CLI creates it:
npx wrangler pages project create mizushi --production-branch main
The GUI doesn't allow creating an empty project — it requires connecting to GitHub or uploading files. The API/CLI route avoids this chicken-and-egg problem.
Once the project exists, GitHub Actions can deploy to it directly without Pages needing its own GitHub connection.
Final Pipeline
flowchart LR
A[git push] --> B[GitHub Actions]
B --> C[checkout]
C --> D[setup Rust]
D --> E["cargo run -- export"]
E --> F["wrangler pages deploy"]
F --> G[Cloudflare Pages]
Total CI time: ~2-3 minutes. Cost: $0 (Cloudflare free tier + GitHub Actions free tier).