跳到內容

Containers on Workers

查證日期
驗證環境wrangler@4.118.0·@cloudflare/containers@0.3.7·compatibility_date: 2026-07-24

⚠️ Workers Paid only。 每一頁 Containers 文件都掛著「Available on Workers Paid plan」。

前面二十八章都在談「怎麼在 isolate 的限制裡把事情做好」。這一章談相反的問題:什麼時候該讓工作掉出 isolate。

第 27 章已經給了一個很硬的例子 —— Workers 上沒有 eval、不能在執行期編譯 WebAssembly、原生模組連 build 都過不了。所以 ffmpeg、ImageMagick、Pandoc、既有的 Python 服務、任何依賴原生二進位的東西,在 isolate 裡是絕對做不到的。

Containers 就是這個逃生門:由 Worker 觸發、由 Cloudflare 排程、跑在你附近的一個真正的 Linux 容器。

Containers 與 Sandboxes 在 2026-04-13 GA


29.1 心智模型:容器永遠有一扇 Durable Object 前門

Section titled “29.1 心智模型:容器永遠有一扇 Durable Object 前門”

這是設定時最多人搞錯的地方,所以先講清楚。

你不能直接綁定一個容器。 容器一定掛在一個 Durable Object 類別後面 —— 那個 DO 就是容器的生命週期管理者、單一序列化點、以及你的程式碼唯一能碰到的東西。

{
"containers": [
{
"class_name": "MediaContainer", // ← 指向下面那個 DO 類別
"image": "./container/Dockerfile",
"instance_type": "basic",
"max_instances": 5
}
],
"durable_objects": {
"bindings": [{ "name": "MEDIA", "class_name": "MediaContainer" }]
// ↑ 出現在 env 上的是這個 ↑ 必須與上面一致
},
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["MediaContainer"] }]
}

三個名字,兩種角色:

設定位置意義
containers[].class_nameMediaContainer哪個 DO 類別管這個容器
durable_objects.bindings[].class_nameMediaContainer必須完全一致
durable_objects.bindings[].nameMEDIAenv 上出現的名字

實測 wrangler types 產生的結果證實了這件事:

MEDIA: DurableObjectNamespace /* MediaContainer */;

env 上沒有任何叫 container 的東西。 容器不是一種 binding 類型,它是 DO 的一個屬性。

wrangler 會抓到什麼、不會抓到什麼

Section titled “wrangler 會抓到什麼、不會抓到什麼”

實測四種設定錯誤,跑 wrangler deploy --dry-run

錯誤結果
durable_objectsclass_name 打錯明確錯誤Your Worker depends on the following Durable Objects, which are not exported in your entrypoint file: SomethingElse.
new_classes 而非 new_sqlite_classes通過,沒有任何警告
完全沒有 durable_objects binding通過,沒有任何警告
完全沒有 migrations 區塊⚠️ 警告,但建議的是新的 declarative exports(第 14 章)而不是 migrations

只有「DO 類別沒 export」這一種會被擋下來。 忘了寫 DO binding、或者用錯 migration 種類,--dry-run 都放行 —— 你的程式碼裡 env.MEDIAundefined,或者容器起不來,而 build 是綠的。

一個沒有記載、但會嚴重影響 CI 的行為:image 指向本機 Dockerfile 時,wrangler deploy --dry-run 會實際執行 docker build。

實測在沒有 registry 存取權的環境裡跑 --dry-run

#2 ERROR: failed to do request: Head "https://registry-1.docker.io/v2/library/node/manifests/22-alpine": Forbidden
ERROR: failed to build: failed to solve: node:22-alpine: failed to resolve source metadata

也就是說 --dry-run 需要 Docker daemon 在跑、需要能拉 base image。這和一般人對 --dry-run(「只檢查設定,不做副作用」)的預期完全不同。

image 改成一個預先建好的 image URI(registry.cloudflare.com/...)就會跳過 build —— 這是 CI 上做設定驗證的實用技巧。Containers 文件裡完全沒有提到 --dry-run


Terminal window
npm i @cloudflare/containers # 實測最新版 0.3.7(2026-06-04)
import { Container, getContainer } from "@cloudflare/containers";
export class MediaContainer extends Container<AppEnv> {
defaultPort = 8080; // 必須與 image 裡 EXPOSE / listen 的 port 一致
sleepAfter = "2m"; // 預設 "10m"
envVars = { CH29_ROLE: "media" };
enableInternet = false;
allowedHosts = ["r2.cloudflarestorage.com"];
override onStart() { /* ... */ }
override onStop(params: { exitCode: number; reason: string }) { /* ... */ }
override onError(error: unknown) { return error; }
/** 一般的 RPC 方法(第 18 章),不用經過容器的 HTTP */
async describe() {
return { defaultPort: this.defaultPort, state: await this.getState() };
}
}

Container 繼承自 DurableObject,所以第 14 到 17 章關於 DO 的一切都適用 —— 包含 this.ctx.storage、alarm、以及 input gate 的序列化保證。

實測 shipped source:

const DEFAULT_SLEEP_AFTER = '10m'; // lib/container.js:20

onActivityExpired 的基底實作:

async onActivityExpired() {
console.log('Activity expired, signalling container to stop');
if (!this.container.running) return;
await this.stop();
}

這一點常被講錯(包括本系列大綱初稿)。 常見說法是「onActivityExpired() 必須呼叫 stop()destroy(),否則容器洩漏」。準確的說法是:

基底實作已經幫你呼叫 stop() 了。 只有當你覆寫它並且忘記停止時才會洩漏。官方 container-class 文件也是這樣寫的:「The default implementation calls stop()」,並附上警告:「If you override onActivityExpired(), call await this.stop() or await this.destroy(). Otherwise, the container does not go to sleep.」

所以要不就別覆寫,要覆寫就記得 await super.onActivityExpired()。範例專案示範的是後者。

stop()SIGTERMdestroy() 直接送 SIGKILLonStop() 在程序結束後執行,收到 exitCodereason'exit' | 'runtime_signal')。

平台層面另有一條:容器要被關閉時「會收到 SIGTERM,15 分鐘後才送 SIGKILL」。所以你的程序有很寬裕的時間收尾。

這是我在寫這一章時修正自己認知的地方。實測 shipped source:

enableInternet = true; // lib/container.js:325

官方 outbound-traffic 頁也是這樣寫:「By default, a Container will allow internet access, and you can set deniedHosts to disallow specific hosts or IPs.」

如果你的容器要處理不可信的輸入(使用者上傳的檔案、外部 URL),請主動關掉它並用 allowedHosts 開白名單:

enableInternet = false; // deny by default
allowedHosts = ["r2.cloudflarestorage.com"]; // 只放行需要的

官方對 allowedHosts 的說明:「當 allowedHosts 被設定時,它會變成 deny-by-default 的白名單。」

一整組出乎意料完整的 egress 攔截 API

Section titled “一整組出乎意料完整的 egress 攔截 API”

@cloudflare/containers 0.3.7 的型別裡有一組很大的出站流量控制介面,而且它是有文件的 —— 只是文件在 /containers/platform-details/outbound-traffic/,不在 Container class 參考頁,所以很容易錯過:

API作用
deniedHosts黑名單,支援 glob
allowedHosts白名單(設了就變 deny-by-default)
interceptHttps攔截 HTTPS。會在容器裡產生 /etc/cloudflare/certs/cloudflare-containers-ca.crt,容器必須信任它
static outbound全域攔截 handler,簽章 (request, env, ctx)
static outboundByHost每個 host 一個 handler(優先於 outbound
static outboundHandlers具名 handler,可在執行期指派
setOutboundHandler() / setOutboundByHost() / setAllowedHosts() / allowHost() / denyHost()執行期動態調整

這組 API 真正厲害的地方在 container → Worker 的方向。官方 workers-connections 頁:

「outbound handler 攔截來自容器的 HTTP 請求,並在 Workers runtime 裡執行,那裡你所有設定好的 binding 都可用。」 容器打一個虛擬 hostname(例如 http://my.kv/some-key),而**「容器內部不需要任何 SDK 或 client library」**。

換句話說:你可以讓一個完全不知道 Cloudflare 存在的既有服務,透過普通的 HTTP 呼叫拿到 KV、R2、D1。 對「把既有 Docker 服務搬上來」這件事,這是最有價值的一個能力,而且它藏在一個大部分人不會點進去的頁面裡。


實測 getContainer(env.MEDIA, key) 直接編譯失敗:

error TS2345: Argument of type 'DurableObjectNamespace<undefined>' is not assignable to
parameter of type 'DurableObjectNamespace<Container<Env>>'.

原因和第 18、26 章一模一樣 —— wrangler types 產生的是:

MEDIA: DurableObjectNamespace /* MediaContainer */;

類別名在註解裡。修法也一樣,手寫一層:

src/env.ts
import type { MediaContainer } from "./index";
export interface AppEnv extends Omit<CloudflareBindings, "MEDIA"> {
MEDIA: DurableObjectNamespace<MediaContainer>;
}

這是本系列第三次遇到同一個問題(第 18 章 service binding、第 26 章 monorepo、本章 container)。wrangler types 對任何「binding 指向一個你自己寫的類別」的情況,都只會把類別名寫進註解。 記住這個 pattern,遇到就直接寫 env.ts

import { getContainer, getRandom, loadBalance } from "@cloudflare/containers";
getContainer(env.MEDIA, "tenant-42") // 依 key 決定實例 -> 同 key 同容器(熱的)
await loadBalance(env.MEDIA, 3) // 在 N 個實例間分散
getRandom(env.MEDIA, 3) // 隨機挑一個

選哪一個取決於你的工作有沒有「親和性」:

  • 有狀態或有快取(同一個租戶的模型、同一個專案的編譯快取)→ getContainer(ns, key),同 key 命中同一個熱容器。
  • 無狀態的純運算(轉檔、產 PDF)→ loadBalance,避免所有請求排隊在同一個實例上。

config-schema.json 的描述字串抄下來(這是最準確的來源):

類型vCPU記憶體磁碟
lite1/16256 MiB2 GB
basic1/41 GiB4 GB
standard-11/24 GiB8 GB
standard-216 GiB12 GB
standard-328 GiB16 GB
standard-4412 GiB20 GB

預設是 lite。上限就是 standard-4

修正兩件事。 本系列大綱初稿寫「舊的 dev 與裸 standard 已不存在」—— 不對。

官方 limits 頁的說法是:「devstandard instance type 為了向後相容而保留,分別是 litestandard-1別名。」

而實測 wrangler deploy --dry-runinstance_type: "dev",拿到的是一個明確的警告而非錯誤:

▲ WARNING - The "dev" instance_type has been renamed to "lite" and will be
removed in a subsequent version. Please update your configuration to use "lite" instead.

所以:是別名 + 棄用警告,不是移除。

自訂 instance type:schema 與文件不一致

Section titled “自訂 instance type:schema 與文件不一致”

schema 允許一個物件形式:

"instance_type": { "vcpu": 2, "memory_mib": 8192, "disk_mb": 16000 }

實測 --dry-run 接受。約束(官方):最少 1 vCPU、最多 4 vCPU、最多 12 GiB 記憶體、最多 20 GB 磁碟,且「每 vCPU 最少 3 GiB 記憶體」、「每 1 GiB 記憶體最多 2 GB 磁碟」。

⚠️ 這裡有一個 schema 與文件的矛盾。 config-schema.jsoninstance_type 描述最後一句寫著:

「Customers on an enterprise plan have the additional option to set custom limits.」

但官方 changelog(2026-01-05)說自訂 instance type 已經「available to all users」。wrangler 內建的 schema 描述是舊的。 以 changelog 為準,但部署前自己確認。

資源Workers Paid 含量/月超出
記憶體25 GiB-hours$0.0000025 / GiB-秒
CPU375 vCPU-minutes$0.000020 / vCPU-秒
磁碟200 GB-hours$0.00000007 / GB-秒

egress:北美與歐洲 $0.025/GB(含 1 TB);大洋洲/韓國/台灣 $0.05/GB(含 500 GB);其他 $0.04/GB(含 500 GB)。

計費的關鍵區分,官方原文:

「CPU usage is based on active usage only」,而「Memory and disk usage are based on the provisioned resources for the instance type you select.」

也就是說 —— CPU 按實際使用計費,但記憶體與磁碟按「你選的 instance type 的配置量」計費,只要容器醒著就在算。

這句話推導出一個很重要的成本結論:閒置的容器仍然在燒記憶體與磁碟的錢,只有 CPU 是免費的。 所以 sleepAfter 不是一個「反正閒著也不花錢」的參數,它直接決定你的記憶體帳單。範例把它設成 "2m" 而不是預設的 "10m",理由就在這裡。

粗略換算:一個 standard-1(4 GiB)閒置 10 分鐘 = 4 × 600 × $0.0000025 = $0.006。看起來很小,但乘上一天幾千次的觸發就不是了。

粒度:「每 10ms 主動執行時就計費一次」。開始於「請求送到容器時,或手動啟動時」,停止於「容器實例睡著之後」。

另一個修正。 大綱初稿寫「GA 起改為 active-CPU 計價」。實際上這個改變是 2025-11-21 的 changelog(「New CPU Pricing for Containers and Sandboxes」),比 2026-04-13 的 GA 早了快五個月。GA 那篇只是重述。

Containers 還會連帶產生 Workers 與 Durable Objects 的費用,那些是分開計算的。


限制
每帳號並行記憶體6 TiB
每帳號並行 vCPU1,500
每帳號並行磁碟30 TB
每帳號 image 儲存總量50 GB
單一 image 大小「受所選 instance type 的可用磁碟限制」
max_instances(每個 container app)預設 20
冷啟動「經常落在 1-3 秒,但取決於 image 大小與程式碼執行時間」
關閉流程SIGTERM → 15 分鐘後 SIGKILL

注意帳號層級的上限是用資源量(記憶體/vCPU/磁碟)表示的,不是實例數。 而且這組數字是 2026-02-25 那次「15 倍提升」之後的值 —— 更早的教學會寫 400 GiB / 100 vCPU / 2 TB(2025-09)甚至 40 GiB / 20 vCPU / 100 GB。

架構要求: 官方唯一的一句話是「Containers should be built for the linux/amd64 architecture」。

⚠️ 文件沒有說在 arm64(Apple Silicon)上建置會發生什麼事。 沒有錯誤訊息、沒有 --platform linux/amd64 的指引、沒有任何 Apple Silicon 的註記。本章不臆測失敗形式,但實務上請在 Dockerfile 或 build 指令裡明確指定平台。

沒有 GPU。 我逐字搜尋過 Containers 的全文,“GPU” 一次都沒有出現;limits 頁沒有 GPU instance type,FAQ 也沒有。

網路上唯一相關的資料是 Cloudflare 2024-09-27 的一篇部落格〈Our container platform is in production. It has GPUs.〉。那篇講的是 Cloudflare 的內部平台(驅動 Workers AI、Workers Builds、Browser Rendering 的那個),文中自己寫著「we’re not ready (quite yet) to open up the platform to everyone」。不要拿它當作公開產品有 GPU 的證據。

需要 GPU 的話,第 34 章的 Workers AI 才是那條路。


Docker 是必需的。 官方:「要在本機開發啟用 Container 的 Worker,你必須先確保安裝了 Docker 相容的 CLI 工具與 Engine。」部署時也一樣:「執行 wrangler deploy 時,你必須有 Docker 在本機執行。」

image 指向本機路徑時,wrangler 會用那個 Dockerfile 建置。dev session 中按 [r] 可以重建。

registry 支援在兩種 dev runner 之間不一樣(官方原文):

「使用 wrangler dev 時,本機開發支援來自 Cloudflare Registry、Docker Hub、Amazon ECR 的 image 參照。使用 vite dev 時,支援 Docker Hub 與 Amazon ECR 這類外部 registry,但 vite dev 無法直接從 Cloudflare Registry 拉取。」

支援的 registry:

  • registry.cloudflare.com(Cloudflare 託管,背後是 R2)
  • Docker Hub(public + private,2026-03-24 起)
  • Amazon ECR(僅 private)
  • Google Artifact Registry(僅 *-docker.pkg.dev,2026-07-01 起)
  • GHCR 不在支援清單上(也沒有明文否認)

私有 registry 認證走 wrangler containers registries configure,憑證存在 Secrets Store。

image_build_context(預設是 image 所在目錄)與 image_vars(等同 docker build --build-arg)這兩個欄位只記載在 Workers 的 Wrangler 設定參考頁,不在 Containers 文件裡


Worker 程式碼與容器實例不是同時更新的。官方 rollouts 頁:

「Worker 程式碼會立即更新,而 Container 實例採用滾動部署策略更新。」

rollout_step_percentage 預設是 [10, 100] —— 先更新 10%,再更新剩下的 90%。也可以給單一數字或陣列。rollout_active_grace_period(預設 0)是「一個活躍的容器實例變成可更新之前,最少要等待的秒數」。

想要立刻全量:wrangler deploy --containers-rollout=immediate

官方那句警告值得原樣抄進你的 code review checklist:

「因為 Worker 程式碼會立即更新、而容器實例是逐步 rollout,請在 rollout 完成前,讓變更在兩個版本之間保持向後相容。」

具體來說:如果你同時改了 Worker 送進容器的請求格式和容器的解析邏輯,rollout 期間新 Worker 會打到舊容器。這和資料庫 migration 的 expand-contract 是同一個問題。

"constraints": {
"regions": ["WNAM", "ENAM"],
"jurisdiction": "eu"
}

九個 region:ENAMWNAMEEURWEURAPACSAMMEOCAFR。官方警告:「容量有限的 region(ME、OC、AFR)不能單獨使用。」

jurisdiction 支援 eu(→ EEUR、WEUR)與 fedramp(→ ENAM、WNAM)。同時指定兩者時,region 必須在該 jurisdiction 的範圍內。

config-schema.jsonContainerApp 有幾個官方文件完全沒提到的欄位:

欄位狀態
scheduling_policydefault / moon / regional文件完全查無
trusted_user_ca_keys文件完全查無

這兩個存在於 wrangler / Cloudchamber 的 API schema,但在 developers.cloudflare.com 上搜不到任何說明。不要用它們 —— 沒有文件的設定沒有相容性承諾。


這是一個很多人不知道存在的能力:

"containers": [{
"class_name": "MediaContainer",
"image": "./container/Dockerfile",
"authorized_keys": ["ssh-ed25519 AAAA..."]
}]
Terminal window
npx wrangler containers instances # 找到 instance id
npx wrangler containers ssh <INSTANCE_ID>

只支援 ssh-ed25519 金鑰類型。安全模型官方原文:

「SSH 不會在 Container 上開放一個公開可存取的 port。唯一的連線方式是透過 Wrangler 的 wrangler containers ssh,它會對你的 Cloudflare 帳號進行認證。」

也支援 ProxyCommand(2026-05-28 起):

Terminal window
ssh -o ProxyCommand="wrangler containers ssh %h" cloudchamber@<INSTANCE_ID>

⚠️ ssh.enabled 的預設值,文件與 schema 打架。

官方 SSH 頁:「ssh.enabled 屬性只控制你能否透過 Wrangler SSH 進 Container。它預設為 true。」

config-schema.json 裡:

"enabled": { "type": "boolean", "description": "...", "default": false }

兩者矛盾。 SSH 是 2026-03-12 加入、2026-05-12 改為預設啟用的;看起來 schema 的預設值沒跟上。如果你要明確關掉它,就明確寫 "ssh": { "enabled": false },不要依賴任何一邊的預設。


@cloudflare/sandbox(實測最新版 0.12.4,2026-07-21)常常和 Containers 被混為一談。它們的關係是:

「Sandbox SDK 讓你在隔離的環境裡安全地執行不可信的程式碼」,而且「建立在 Containers 之上」。

  • Containers = 你自己的 image、你自己的服務。
  • Sandboxes = 執行別人的程式碼(AI agent 產生的、使用者提交的),有 Python/JS/TS 的持久 code interpreter、預覽 URL、瀏覽器 PTY 終端、workspace 快照。

兩者同一天 GA(2026-04-13),Sandboxes 的計費走底下 Containers 的價目。有自己獨立的文件樹(/sandbox/)。

選擇很單純:執行的是你自己寫的東西 → Containers;執行的是你不信任的東西 → Sandboxes。


29.10 LinkForge:什麼時候用、什麼時候不用

Section titled “29.10 LinkForge:什麼時候用、什麼時候不用”

LinkForge 目前為止的功能,沒有一個需要容器。這一點本身就是結論 —— 容器是逃生門,不是預設選項。

會需要的情況:

需求為什麼 isolate 做不到
批次產生高解析度 QR PNG需要原生影像函式庫
把分析報表輸出成 PDFLaTeX / wkhtmltopdf 是原生二進位
匯入使用者上傳的 CSV/Excel大檔案超過 128 MB 記憶體上限(第 12 章)
執行使用者提供的 webhook 轉換腳本第 27 章:沒有 eval、不能執行期編譯 WASM → 而且這應該用 Sandboxes

正確的形狀:Queue → Container,不是 Request → Container

Section titled “正確的形狀:Queue → Container,不是 Request → Container”
// ❌ 不要:使用者按下按鈕,同步等一個 1-3 秒冷啟的容器
app.post("/api/export", async (c) => {
const stub = getContainer(c.env.MEDIA, c.get("session").tenantId);
return stub.fetch(new Request("http://container/pdf")); // 使用者等好幾秒
});
// ✅ 要:立刻回應,工作丟給 Queue(第 19 章),consumer 才碰容器
app.post("/api/export", async (c) => {
const jobId = crypto.randomUUID();
await c.env.EXPORTS.send({ jobId, tenantId: c.get("session").tenantId });
return c.json({ jobId }, 202);
});
// Queue consumer
export default {
async queue(batch: MessageBatch<ExportJob>, env: AppEnv) {
for (const msg of batch.messages) {
// 依租戶分流 -> 同一個租戶的工作命中同一個熱容器
const stub = getContainer(env.MEDIA, msg.body.tenantId);
const res = await stub.fetch(new Request("http://container/pdf", {
method: "POST", body: JSON.stringify(msg.body),
}));
if (!res.ok) { msg.retry(); continue; }
await env.BUCKET.put(`exports/${msg.body.jobId}.pdf`, res.body);
msg.ack();
}
},
};

理由有三個,全部來自本章與前面章節的實測:

  1. 冷啟 1-3 秒。 放在請求路徑上就是 1-3 秒的 p99。
  2. Queue 天生就有重試與 DLQ(第 19 章),容器崩掉時你不必自己實作。
  3. 依租戶當 key 讓同一個租戶的連續工作命中熱容器,攤掉冷啟成本 —— 這正是 getContainer(ns, key) 而非 loadBalance 的理由。

再加上一條成本上的理由:sleepAfter 決定記憶體帳單(29.4),而批次化的工作會讓容器「醒著的時間集中」,比零星喚醒划算得多。


#結論影響
1容器一定掛在 DO 後面;env 上出現的是 DO binding 的 name,不是 class_name最常見的設定錯誤
2wrangler types 產生 MEDIA: DurableObjectNamespace /* MediaContainer */;類別名在註解裡
3getContainer(env.MEDIA, k) 因此編譯失敗(TS2345),要手寫 env.ts本系列第三次遇到型別不流動(第 18、26 章)
4DO class_name 打錯 → wrangler 明確報錯唯一會被擋下來的設定錯誤
5new_classes 而非 new_sqlite_classes--dry-run 通過,無警告靜默失敗
6完全沒有 durable_objects binding → --dry-run 通過,無警告env.MEDIA 會是 undefined
7沒有 migrations → 警告建議改用 declarative exports(第 14 章)migrations 不是唯一寫法
8--dry-run 會真的執行 docker buildCI 需要 Docker + registry 存取;Containers 文件從未提及 --dry-run
9改用預建 image URI 可跳過 buildCI 驗證設定的實用技巧
10sleepAfter 預設 "10m"(shipped source 的 DEFAULT_SLEEP_AFTER
11onActivityExpired 基底實作已呼叫 this.stop()「不呼叫就洩漏」的說法只在你覆寫時成立
12enableInternet 預設是 true(source 與文件一致)處理不可信輸入時要主動關掉
13完整的 egress 攔截 API 有文件,但在 /platform-details/outbound-traffic/容易錯過;container→Worker 呼叫不需要容器內裝 SDK
14dev / standardlite / standard-1別名,不是移除實測 dev 得到明確的改名警告
15上限是 standard-4:4 vCPU / 12 GiB / 20 GB預設是 lite
16自訂 instance type 物件形式 --dry-run 接受
17schema 說自訂尺寸是 enterprise-only,changelog 說已對所有人開放wrangler 內建 schema 描述過時
18CPU 按實際使用計費;記憶體與磁碟按 instance type 的配置量計費閒置容器仍在燒記憶體錢 → sleepAfter 是成本參數
19active-CPU 計價是 2025-11-21 上線,不是 GA 時比 GA 早近五個月
20帳號上限以資源量表示:6 TiB 記憶體 / 1,500 vCPU / 30 TB 磁碟;image 儲存 50 GB2026-02-25 的 15 倍提升後值
21冷啟「經常 1-3 秒」;關閉是 SIGTERM → 15 分鐘 → SIGKILL不要放在請求路徑上
22架構必須 linux/amd64arm64 上會怎樣,文件完全沒說明確指定 --platform
23沒有 GPU(全文搜尋 0 次命中)2024 年那篇 GPU 部落格講的是內部平台
24wrangler devvite dev 的 registry 支援不同 —— vite dev 拉不了 Cloudflare Registry
25GHCR 不在支援清單上也未明文否認
26rollout 期間 Worker 立即更新、容器逐步更新變更必須跨版本向後相容
27scheduling_policy(含 moon)與 trusted_user_ca_keys 在 schema 裡但文件查無不要使用
28ssh.enabled 預設值,文件說 true、schema 說 false明確寫出來,不要靠預設
29Sandboxes 是建在 Containers 上的另一個產品(0.12.4),有獨立文件樹執行不可信程式碼用它

  1. 故意把 migrationsnew_sqlite_classes 改成 new_classes,確認 --dry-run 真的不會擋,然後部署看它在哪一步失敗。
  2. 用預建 image URI 取代 Dockerfile 路徑,量測 --dry-run 的時間差 —— 這就是 29.1 那個 CI 技巧的價值。
  3. enableInternet = false 加一個 allowedHosts,在容器裡 curl 一個不在白名單的 host,觀察失敗形式。
  4. static outboundByHost 讓容器透過 http://my.kv/<key> 讀 KV,驗證「容器內不需要任何 SDK」這個說法。
  5. sleepAfter"10m" 改成 "30s",用 dashboard 的 metrics 比較一天下來的 GiB-hours 差異。
  6. 在 Apple Silicon 上不加 --platform 建一個 image 並部署,記錄實際的失敗訊息 —— 文件沒寫,這個資訊值得寫成 issue 回報。

下一章(第 30 章):Browser Run(原 Browser Rendering)—— 當你需要的不是任意 Linux 而是一個 headless 瀏覽器時,平台已經幫你託管好了。