從 Pages 到 Workers — SuperPortia 的 Cloudflare 部署演化
SuperPortia 在 Cloudflare 上跑著 4 個靜態站、3 個 Worker、以及 D1 + Vectorize 儲存層。月費:$0。這篇文章記錄這套架構是怎麼長出來的,以及 Astro 被 Cloudflare 收購之後,新專案的部署方式發生了什麼變化。
全景:今天的狀態
從 registry.json 可以數出三種部署在 Cloudflare 上的模式:
cloud-serverless(Workers):
- Cloud UB Worker — 知識 API,
ub.superportia.dev - Email Intake Worker — Gmail 輪詢 + AI 分類,cron every 3 min
- Scout Worker — 定時情報收集,
scout.superportia.dev
local-build-cloud(需本地建置後部署):
- Bridge — Next.js 15,OpenNext → Cloudflare Pages,
bridge.superportia.dev - Reading — Astro SSR,
@astrojs/cloudflareadapter → Workers,read.superportia.dev
cloud-static(Pages auto-deploy):
- Docs Site — Docusaurus,
docs.superportia.dev - Blog —
blog.superportia.dev - Catmints Cafe — 靜態 HTML,
cafe.superportia.dev
儲存層橫跨多個 Worker 共用:D1 SQL 資料庫、Vectorize 向量索引、Workers AI(embedding 生成)。
這個組合在 Cloudflare 的免費層上完全跑得起來——後面的章節會把具體額度列出來。
Pages vs Workers:Astro 收購改變了預設答案
SuperPortia 的早期靜態站全部部署在 Cloudflare Pages。Pages 提供 Git 連接、自動建置、免費 CDN,是當時最省事的選擇。Workers 則用於後端邏輯,例如最早期的 UB API。
這個局面在 2026 年 1 月發生了根本性的轉變。
2026-01-16,Cloudflare 宣布收購 Astro。@astrojs/cloudflare adapter 隨後更新,預設部署目標從 Pages 改為 Workers。執行 astro add cloudflare 時,產生的是 wrangler.jsonc + Workers 設定,而不是以前的 Pages 設定。
官方立場很清楚:Cloudflare 建議新專案使用 Workers,Pages 是遺留介面。
為什麼 Workers 比 Pages 更好?
| 面向 | Pages | Workers |
|---|---|---|
| 儲存 binding | 有限制 | 完整支援 D1、KV、R2、Vectorize、Queues、Durable Objects |
| 請求處理 | 靜態 CDN 為主 | 完整 middleware、caching 策略、edge compute |
| 本地開發 | Node.js 模擬 | 真實 workerd runtime,dev 和 prod 行為一致 |
| 部署指令 | wrangler pages deploy | wrangler deploy(更快更簡潔) |
| 未來投資方向 | 維護模式 | Cloudflare 主要開發投入 |
對 SuperPortia 的新 Astro 6 專案(Reading、Agentic、未來的 UB Dashboard),結論是明確的:Workers。
現有的 Bridge 和靜態站則繼續留在 Pages,直到有足夠的理由遷移為止。Bridge 使用 Next.js 15 而非 Astro,這個轉變不直接影響它。
Workers 實際長什麼樣:Cloud UB 的架構
Cloud UB Worker 是 SuperPortia 最複雜的 Worker,也是理解整體架構的最好案例。
wrangler.toml 的實際內容:
name = "superportia-ub"
main = "worker.js"
compatibility_date = "2025-01-01"
workers_dev = true
[[d1_databases]]
binding = "DB"
database_name = "superportia-ub"
database_id = "166954b9-e07e-4911-8aeb-d783a7f792d0"
[ai]
binding = "AI"
[[vectorize]]
binding = "VECTORIZE"
index_name = "superportia-ub-vectors"
三個 binding 在一個 Worker 裡:
DB→ D1 SQLite,存放entries(UB Dock 碼頭)、classified_entries(正區)、email_intelAI→ Workers AI binding(@cf/...模型);實際 embedding 用gemini-embedding-001(Google API,透過fetch呼叫,非[ai]binding 直接掛載的模型)做 768 維向量VECTORIZE→ Vectorize 索引,做語義搜尋
自訂域名 ub.superportia.dev 透過 CF Dashboard 設定(DNS + SSL 自動處理),不需要寫進 wrangler.toml。
Email Intake Worker 的架構更單純——它只有 cron handler,沒有 HTTP routing。*/3 * * * * 每三分鐘執行一次,輪詢 Gmail API、AI 分類、寫入 email_intel 表。對外只有 superportia-email-intake.wishia.workers.dev,但實際上從不處理入站 HTTP 請求。
部署方式:wrangler、GitHub Actions、自動部署
SuperPortia 使用三種部署路徑,對應不同場景:
1. wrangler deploy(本地直接部署)
適合快速迭代或一次性部署:
# 設定好環境變數後直接執行
wrangler deploy
CLOUDFLARE_API_TOKEN 和 CLOUDFLARE_ACCOUNT_ID 從 ~/.zshenv 自動載入。
2. GitHub Actions + cloudflare/wrangler-action
Cloud UB Worker 走這條路:推送到 main,GitHub Actions 自動執行 wrangler deploy。需要在 repo 設定兩個 Secrets:CLOUDFLARE_API_TOKEN 和 CLOUDFLARE_ACCOUNT_ID。
對生產服務而言,這是正確的模式——每次 push 有完整的 CI log,可審計、可回溯。
3. Cloudflare Pages auto-deploy(Git 連接)
Docs Site、Catmints Cafe、Blog 使用這條路。在 CF Dashboard 連接 GitHub repo,設定建置指令,之後每次 push 到 main 自動建置部署。最省事,但僅適合靜態站。
deploy-gate hook
Ops repo 有一個 pre-commit hook,防止意外部署。任何包含 wrangler deploy 或影響生產設定的變更,都需要明確確認才能執行。這個 gate 在多次 SSH session 遠端操作中避免了誤觸部署。
免費層現實:$0/month 能跑什麼
這是最常被問到的問題。以下是 Cloudflare 免費層的實際額度,以及 SuperPortia 目前的用量情況:
| 服務 | 免費額度 | SuperPortia 用量 |
|---|---|---|
| Workers | 100,000 req/day | UB API + Email Intake + Scout,日流量遠低於上限 |
| D1 | 5 GB storage,5M rows read/day | UB entries + email_intel,目前數百 MB |
| Vectorize | 5M vectors,30M dimensions queried/day | UB vectors,目前數萬筆 |
| Workers AI | 有每日 neurons 限制 | Embedding 生成,每次 ingest 用一次 |
| Pages | 無限靜態站,500 builds/month | 4 個靜態站,deploy 頻率低 |
| R2 | 10 GB storage,1M Class A ops/month | 目前未啟用,規劃作圖片儲存 |
| CF Tunnel | 免費(cloudflared) | SiYuan Notes 透過 tunnel 對外 |
實際上,Workers AI 的 neurons 限制是最可能碰到天花板的地方(若使用 @cf/... 模型)。每次 ingest_fragment 呼叫都會用 gemini-embedding-001(透過 Google API fetch)生成一個 768 維向量。如果某天大量 batch ingest,Workers AI 可能會被限速。目前的因應方式是 MTAAA pipeline 的 boiler_grandpa_v2.py 分批執行,避免單次 burst。
D1 的 5GB 免費額度對知識庫來說非常充裕。SQLite 格式壓縮效率高,UB 的 entries + classified_entries 即使成長到數十萬筆也不會超過上限。
遷移路線:哪些留 Pages,哪些移 Workers
基於目前的架構和 Cloudflare 的發展方向,SuperPortia 的遷移策略如下:
繼續留在 Pages(有原因才遷移):
- Bridge — Next.js 15,OpenNext 對 CF Pages 支援成熟,遷移收益小
- Docs Site / Blog / Catmints Cafe — 純靜態,Pages 完全夠用,auto-deploy 省事
已在 Workers(正確位置):
- Cloud UB Worker、Email Intake、Scout Worker — 這三個本來就只能是 Workers
新專案預設 Workers:
- Reading — 已是 Astro SSR on Workers
- Agentic(v2)— 開發中,將部署 Workers
- UB Dashboard(計劃中)— Astro 6 on Workers,可用 D1 native binding 直接讀 UB 資料
一個值得注意的細節:Astro 6 的 @astrojs/cloudflare adapter 現在使用真實的 workerd runtime 跑本地開發,這意味著 D1 binding 在 wrangler dev 階段就能連到 remote D1,和生產行為一致。舊的 Pages 路徑依賴 Node.js 模擬,有時在生產才發現 runtime 不相容的問題。Workers 路徑在開發階段就把這個坑填掉了。
為什麼沒有使用自己的伺服器
一個自然的問題:為什麼不在自己的 VPS 或 NAS 上跑這些服務?
幾個具體原因:
零冷啟動:Workers 在 Cloudflare 全球 300+ 個資料中心執行,沒有傳統 serverless 的冷啟動問題。UB API 的 p99 latency 穩定,不像 Lambda 或 Cloud Functions 那樣有首次喚醒延遲。
綁定生態:D1、Vectorize、KV、R2 作為 binding 直接掛載在 Worker 裡,不需要網路往返。UB Worker 查詢 D1 和 Vectorize 是同一個 runtime 內的 binding 呼叫,延遲比任何外部資料庫都低。
維運成本:在 $0/month 的條件下,不需要管理 OS 更新、SSL 憑證、反向代理設定、監控告警基礎設施。這些維運工作時間比技術工作還貴。
對 SuperPortia 這種規模(個人 + 小團隊),Cloudflare Workers 的免費層覆蓋了幾乎所有生產流量,這不是暫時的設計——而是長期的架構選擇。
小結
Pages 是起點,Workers 是方向。這個轉變不是為了趕上新技術,而是因為 Cloudflare 把它的整個儲存 + 計算生態都綁在 Workers 上。
SuperPortia 的部署現況:
- 3 個 Workers 提供後端 API 和排程工作
- 4 個 Pages 站點提供靜態前端
- D1 + Vectorize 提供持久儲存,全部在免費層內
- 新 Astro 6 專案直接走 Workers,不再走 Pages
完整的部署類型說明見 SuperPortia 專案全景 — 29 個專案,3 個維度,Cloud UB 的知識架構(Dock → 正區 雙表設計)見 Cloud UB 相關文章。
...強可用要好得多。 Phase 0 的降規,表面上看是退步,實際上是建立一個可以信任的基礎。一個 score → compare → revert/keep 的最小閉環,比一個充滿假設但從未穩定運行的複雜閉環,有價值一百倍。 我們從四層審查出發,走回了工程的第一原則:先讓它跑得通,再讓它跑得好,最後才讓它跑得快。 相關文章:[[Beast Mode + autoResearch 完整指南]] |...
...k(entries table)後, 的分類管線自動接手分類和提升到正區(classified_entries)。 詳細的 UB 架構可以從這篇了解背景:[[從 Pages 到 Workers — SuperPortia 的 Cloudflare 部署演化]]。 這個場景替代了人工 RSS 閱讀 SuperPortia 每天大概有...