diff --git a/public/__redirects b/public/__redirects index 6bbbb219a1f..f1e0872c48d 100644 --- a/public/__redirects +++ b/public/__redirects @@ -627,10 +627,14 @@ /constellation/ /workers-ai/ 301 # Containers /containers/beta-info/ /containers/faq/ 301 -/containers/container-package/ /containers/reference/container-class/ 301 -/containers/durable-object-methods/ /durable-objects/api/container/ 301 +# Containers API +/containers/container-package/ /containers/api/container-class/ 301 +/containers/durable-object-methods/ /containers/api/durable-object-container/ 301 +/containers/container-class/ /containers/api/container-class/ 301 +/containers/reference/container-class/ /containers/api/container-class/ 301 +/containers/reference/durable-object-methods/ /containers/api/durable-object-container/ 301 +/durable-objects/api/container/ /containers/api/durable-object-container/ 301 # Containers IA rework: loose pages folded into core sections -/containers/container-class/ /containers/reference/container-class/ 301 /containers/local-dev/ /containers/guides/local-dev/ 301 /containers/deploy/ /containers/guides/deploy/ 301 /containers/execute-commands/ /containers/guides/execute-commands/ 301 @@ -642,16 +646,17 @@ # Containers IA rework: platform-details/ dissolved into core sections /containers/platform-details/architecture/ /containers/concepts/architecture/ 301 /containers/platform-details/placement/ /containers/concepts/placement/ 301 -/containers/platform-details/outbound-traffic/ /containers/guides/outbound-traffic/ 301 +/containers/platform-details/outbound-traffic/ /containers/configuration/outbound-traffic/ 301 /containers/platform-details/workers-connections/ /containers/configuration/workers-connections/ 301 /containers/platform-details/environment-variables/ /containers/configuration/environment-variables/ 301 /containers/platform-details/rollouts/ /containers/configuration/rollouts/ 301 /containers/platform-details/scaling-and-routing/ /containers/configuration/scaling-and-routing/ 301 /containers/platform-details/limits/ /containers/platform/limits/ 301 /containers/platform-details/image-management/ /containers/guides/image-management/ 301 -/containers/platform-details/durable-object-methods/ /durable-objects/api/container/ 301 +/containers/platform-details/durable-object-methods/ /containers/api/durable-object-container/ 301 /containers/platform-details/ /containers/concepts/architecture/ 301 # Containers IA rework: Configuration section + Local Development to Guides + Wrangler pages to Reference +/containers/guides/outbound-traffic/ /containers/configuration/outbound-traffic/ 301 /containers/reference/local-dev/ /containers/guides/local-dev/ 301 /containers/reference/environment-variables/ /containers/configuration/environment-variables/ 301 /containers/reference/scaling-and-routing/ /containers/configuration/scaling-and-routing/ 301 diff --git a/src/content/changelog/containers/2026-03-26-outbound-workers.mdx b/src/content/changelog/containers/2026-03-26-outbound-workers.mdx index 80efb1f9b20..4d09b7a470f 100644 --- a/src/content/changelog/containers/2026-03-26-outbound-workers.mdx +++ b/src/content/changelog/containers/2026-03-26-outbound-workers.mdx @@ -78,4 +78,4 @@ This provides an easy way to associate state with any container instance, and in Upgrade to `@cloudflare/containers` version 0.2.0 or later, or `@cloudflare/sandbox` version 0.8.0 or later to use outbound Workers. -Refer to [Containers outbound traffic](/containers/guides/outbound-traffic/) and [Sandboxes outbound traffic](/sandbox/guides/outbound-traffic/) for more details and examples. +Refer to [Containers outbound traffic](/containers/configuration/outbound-traffic/) and [Sandboxes outbound traffic](/sandbox/guides/outbound-traffic/) for more details and examples. diff --git a/src/content/changelog/containers/2026-04-13-sandbox-outbound-workers-tls-auth.mdx b/src/content/changelog/containers/2026-04-13-sandbox-outbound-workers-tls-auth.mdx index f5fe931784a..7b563931f02 100644 --- a/src/content/changelog/containers/2026-04-13-sandbox-outbound-workers-tls-auth.mdx +++ b/src/content/changelog/containers/2026-04-13-sandbox-outbound-workers-tls-auth.mdx @@ -109,4 +109,4 @@ Handlers accept `params`, so you can customize behavior per instance without def Upgrade to `@cloudflare/containers@0.3.0` or `@cloudflare/sandbox@0.8.9` to use these features. -For more details, refer to [Sandbox outbound traffic](/sandbox/guides/outbound-traffic/) and [Container outbound traffic](/containers/guides/outbound-traffic/). +For more details, refer to [Sandbox outbound traffic](/sandbox/guides/outbound-traffic/) and [Container outbound traffic](/containers/configuration/outbound-traffic/). diff --git a/src/content/docs/containers/reference/container-class.mdx b/src/content/docs/containers/api/container-class.mdx similarity index 98% rename from src/content/docs/containers/reference/container-class.mdx rename to src/content/docs/containers/api/container-class.mdx index b3a3c160245..9443c81ee57 100644 --- a/src/content/docs/containers/reference/container-class.mdx +++ b/src/content/docs/containers/api/container-class.mdx @@ -1,16 +1,16 @@ --- pcx_content_type: reference -title: Container Interface +title: Container class sidebar: - order: 1 -description: API reference for the Container interface and utility functions + order: 2 +description: API reference for the higher-level Container class built on Durable Objects. products: - containers --- import { PackageManagers, TypeScriptExample } from "~/components"; -The [`Container` class](https://github.com/cloudflare/containers) from [`@cloudflare/containers`](https://www.npmjs.com/package/@cloudflare/containers) is the most common way to interact with container instances from a Worker. +The [`Container` class](https://github.com/cloudflare/containers) from [`@cloudflare/containers`](https://www.npmjs.com/package/@cloudflare/containers) provides lifecycle helpers for container instances. For direct lifecycle control, use the [Durable Object Container API](/containers/api/durable-object-container/). **`Container` extends [`DurableObject`](/durable-objects/api/base/).** The Durable Object manages routing, persistent state, and lifecycle hooks, while the container process runs your image inside a Linux VM. Because your subclass is a Durable Object, you have access to the full Durable Object API — including [`this.ctx.storage`](/durable-objects/api/sqlite-storage-api/) for persistent SQLite-backed storage and [`this.ctx.id`](/durable-objects/api/id/) for the unique instance identifier. Use Durable Object storage to persist state that should survive container restarts, such as configuration, user data, or task results. @@ -102,7 +102,7 @@ Configure these as class fields on your subclass. They apply to every instance o `true`) — controls whether the container can make outbound HTTP requests. Set to `false` for sandboxed environments where you want to intercept or block all outbound traffic. For more information, refer to [Handle outbound - traffic](/containers/guides/outbound-traffic/). + traffic](/containers/configuration/outbound-traffic/). - **`pingEndpoint`** (`string`, default: `"ping"`) — the host and path the class uses to health-check the container @@ -719,7 +719,7 @@ export default { ``` -For more information, refer to [Handle outbound traffic](/containers/guides/outbound-traffic/). +For more information, refer to [Handle outbound traffic](/containers/configuration/outbound-traffic/). ## Utility functions diff --git a/src/content/docs/containers/api/durable-object-container.mdx b/src/content/docs/containers/api/durable-object-container.mdx new file mode 100644 index 00000000000..4dd87ce737a --- /dev/null +++ b/src/content/docs/containers/api/durable-object-container.mdx @@ -0,0 +1,371 @@ +--- +title: Durable Object Container API +description: Access and manage containers associated with a Durable Object, including start, stop, and interaction methods. +pcx_content_type: concept +sidebar: + order: 1 +products: + - containers + - durable-objects +--- + +import { + Render, + Tabs, + TabItem, + GlossaryTooltip, + Type, + MetaInfo, + TypeScriptExample, +} from "~/components"; + +## Description + +Each [container](/containers/) is managed by a Durable Object. The Durable Object manages routing and persistent state. The container process runs your image inside a Linux VM. + +The API documented on this page is available on `this.ctx.container` inside any Durable Object class that has a container binding. Use it for direct control over the container process. + +You can instead use the [`Container` class](/containers/api/container-class/) from `@cloudflare/containers`. The class adds routing, readiness checks, lifecycle hooks, activity tracking, and scheduling. To compare both APIs, refer to [Containers APIs](/containers/api/). + +Your Durable Object also has access to [SQLite storage](/durable-objects/api/sqlite-storage-api/) through `this.ctx.storage`, [alarms](/durable-objects/api/alarms/), and all other Durable Object APIs. + + +```ts +import { DurableObject } from "cloudflare:workers"; + +interface Env {} + +export class MyDurableObject extends DurableObject { + constructor(ctx: DurableObjectState, env: Env) { + super(ctx, env); + + ctx.blockConcurrencyWhile(async () => { + if (!ctx.container!.running) { + ctx.container!.start(); + } + }); + } +} +``` + + +## Attributes + +### `running` + +`running` returns `true` if the container is currently running. It does not ensure that the container has fully started and ready to accept requests. + +```js +this.ctx.container.running; +``` + +## Methods + +### `start` + +`start` boots a container. This method does not block until the container is fully started. +You may want to confirm the container is ready to accept requests before using it. + +```js +this.ctx.container.start({ + env: { + FOO: "bar", + }, + enableInternet: false, + entrypoint: ["node", "server.js"], +}); +``` + +#### Parameters + +- `options` (optional): An object with the following properties: + - `env`: An object containing environment variables to pass to the container. This is useful for passing configuration values or secrets to the container. + - `entrypoint`: An array of strings representing the command to run in the container. + - `enableInternet`: A boolean indicating whether to enable internet access for the container. + +#### Return values + +- None. + +### `exec` + +`exec` starts another process inside an already-running Container. It does not start a stopped Container. + +The following example calls `this.ctx.container.exec()` inside a class extending `Container` from `@cloudflare/containers`. In RPC methods, check `this.ctx.container.running` and call `await this.start()` when needed. You can also use the `onStart()` hook to run any series of commands whenever the Container starts. + +```ts +exec( + cmd: string[], + options?: ContainerExecOptions, +): Promise +``` + +The `exec` operation starts the executable directly with the provided arguments. It does not start a shell or interpret pipes, redirects, expansion, or other shell syntax. Invoke Bash explicitly with `["bash", "-lc", ""]` when Bash exists in the image. Use `["sh", "-c", ""]` for images with only a Portable Operating System Interface (POSIX) shell. + +The following RPC method starts the Container before executing a command: + + +```ts +import { Container } from "@cloudflare/containers"; + +export class MyContainer extends Container { + async runCommand() { + if (!this.ctx.container.running) { + await this.start(); + } + + const process = await this.ctx.container.exec(["node", "--version"]); + const output = await process.output(); + + return { + pid: process.pid, + exitCode: output.exitCode, + stdout: new TextDecoder().decode(output.stdout), + }; + } +} +``` + + +#### Parameters + +- `cmd` (`string[]`) — executable followed by its arguments. +- `options` (`ContainerExecOptions`, optional) — process configuration: + - `stdin` (`ReadableStream | "pipe"`) — source for standard input. Use `"pipe"` to write through the returned `stdin` stream. When omitted, standard input closes and sends end-of-file (EOF). + - `stdout` (`"pipe" | "ignore"`, default `"pipe"`) — captures or discards standard output. + - `stderr` (`"pipe" | "ignore" | "combined"`, default `"pipe"`) — captures, discards, or merges standard error into standard output. The `"combined"` value requires `stdout: "pipe"`. Combined output does not guarantee ordering between its source streams. + - `cwd` (`string`) — working directory for the process. + - `env` (`Record`) — environment additions and overrides. The process inherits existing Container variables. Matching keys use the per-execution value. + - `user` (`string`) — image user for the process. + +#### Return values + +Returns `Promise`. + +An `ExecProcess` has these fields and methods: + +- `stdin` (`WritableStream | null`) — writable standard input when `stdin` is `"pipe"`. +- `stdout` (`ReadableStream | null`) — readable standard output when piped. +- `stderr` (`ReadableStream | null`) — readable standard error when piped separately. +- `pid` (`number`) — process identifier. +- `exitCode` (`Promise`) — resolves when the process exits. Nonzero codes resolve normally instead of rejecting. +- `output()` (`Promise`) — reads buffered output once. `ExecOutput` contains `stdout` (`ArrayBuffer`), `stderr` (`ArrayBuffer`), and `exitCode` (`number`). Ignored streams produce empty buffers. Use `TextDecoder` to decode text. +- `kill(signal?: number)` (`void`) — queues a signal for the process. The default is `SIGTERM`, signal `15`. The signal must be from `1` through `64`. + +With `stderr: "combined"`, `stderr` is `null` on `ExecProcess` and an empty `ArrayBuffer` on `ExecOutput`. Read both output channels from `stdout`. + +`output()` throws a `TypeError` when called more than once or after either readable stream starts being consumed. For large output, consume both readable streams concurrently instead of buffering them with `output()`. + +`exec` has no built-in timeout. Use `kill()` to request termination, then observe completion through `exitCode`. A process can handle or ignore a signal, so this does not enforce a hard deadline. Do not infer a specific exit code from the signal. + +#### Exceptions + +- `exec()` throws when the Container is not running. +- `exec()` throws a `TypeError` when `cmd` is empty, an option mode is invalid, or `stderr: "combined"` is used with `stdout: "ignore"`. +- `exec()` rejects if the runtime cannot create or start the process. +- Environment variable names cannot contain `=` or null characters. Environment values, `cwd`, and `user` cannot contain null characters. +- `kill()` throws a `RangeError` when the signal is outside the supported range. + +For task-oriented examples, refer to [Execute commands](/containers/guides/execute-commands/). + +### `destroy` + +`destroy` stops the container and optionally returns a custom error message to the `monitor()` error callback. + +```js +this.ctx.container.destroy("Manually Destroyed"); +``` + +#### Parameters + +- `error` (optional): A string that will be sent to the error handler of the `monitor` method. This is useful for logging or debugging purposes. + +#### Return values + +- A promise that returns once the container is destroyed. + +### `signal` + +`signal` sends an IPC signal to the container, such as SIGKILL or SIGTERM. This is useful for stopping the container gracefully or forcefully. + +```js +const SIGTERM = 15; +this.ctx.container.signal(SIGTERM); +``` + +#### Parameters + +- `signal`: a number representing the signal to send to the container. This is typically a POSIX signal number, such as SIGTERM (15) or SIGKILL (9). + +#### Return values + +- None. + +### `setInactivityTimeout` + +`setInactivityTimeout` sets how long a running container can remain inactive before the runtime stops it. + +```ts +setInactivityTimeout(durationMs: number | bigint): Promise +``` + +```js +await this.ctx.container.setInactivityTimeout(10 * 60 * 1000); +``` + +#### Parameters + +- `durationMs`: Inactivity timeout in milliseconds. + +#### Return values + +- A promise that resolves after the timeout is set. + +### `getTcpPort` + +`getTcpPort` returns a TCP port from the container. This can be used to communicate with the container over TCP and HTTP. + +```js +const port = this.ctx.container.getTcpPort(8080); +const res = await port.fetch("http://container/set-state", { + body: initialState, + method: "POST", +}); +``` + +```js +const conn = this.ctx.container.getTcpPort(8080).connect("10.0.0.1:8080"); +await conn.opened; + +try { + if (request.body) { + await request.body.pipeTo(conn.writable); + } + return new Response(conn.readable); +} catch (err) { + console.error("Request body piping failed:", err); + return new Response("Failed to proxy request body", { status: 502 }); +} +``` + +#### Parameters + +- `port` (number): a TCP port number to use for communication with the container. + +#### Return values + +- `TcpPort`: a `TcpPort` object representing the TCP port. This object can be used to send requests to the container over TCP and HTTP. + +### `monitor` + +`monitor` returns a promise that resolves when a container exits and errors if a container errors. This is useful for setting up +callbacks to handle container status changes in your Workers code. + +```js +class MyContainer extends DurableObject { + constructor(ctx, env) { + super(ctx, env); + function onContainerExit() { + console.log("Container exited"); + } + + // the "err" value can be customized by the destroy() method + async function onContainerError(err) { + console.log("Container errored", err); + } + + this.ctx.container.start(); + this.ctx.container.monitor().then(onContainerExit).catch(onContainerError); + } +} +``` + +#### Parameters + +- None + +#### Return values + +- A promise that resolves when the container exits. + +### `interceptOutboundHttp` + +`interceptOutboundHttp` routes outbound HTTP requests matching a hostname, hostname glob, IP address, IP:port, or CIDR range through a `WorkerEntrypoint`. Can be called before or after starting the container. Open connections pick up the new handler without being dropped. + +```js +const worker = this.ctx.exports.MyWorker({ props: { message: "hello" } }); + +// Match a specific hostname +this.ctx.container.interceptOutboundHttp("api.example.com", worker); + +// Match a hostname glob pattern +this.ctx.container.interceptOutboundHttp("*.example.com", worker); + +// Match an IP:port +await this.ctx.container.interceptOutboundHttp("15.0.0.1:80", worker); + +// Match a CIDR range (IPv4 and IPv6) +await this.ctx.container.interceptOutboundHttp("123.123.123.123/23", worker); +``` + +#### Parameters + +- `target` (string): A hostname, hostname glob (for example, `*.example.com`), IP address, IP:port, or CIDR range to match. +- `worker` (WorkerEntrypoint): A `WorkerEntrypoint` instance to handle matching requests. + +#### Return values + +- None. + +### `interceptAllOutboundHttp` + +`interceptAllOutboundHttp` routes all outbound HTTP requests from the container through a `WorkerEntrypoint`, regardless of destination. + +```js +await this.ctx.container.interceptAllOutboundHttp(worker); +``` + +#### Parameters + +- `worker` (WorkerEntrypoint): A `WorkerEntrypoint` instance to handle all outbound HTTP requests. + +#### Return values + +- A promise that resolves once the intercept rule is installed. + +### `interceptOutboundHttps` + +`interceptOutboundHttps` routes outbound HTTPS requests matching a hostname or hostname glob through a `WorkerEntrypoint`. Works the same way as `interceptOutboundHttp` but for HTTPS traffic. The container must trust the CA certificate at `/etc/cloudflare/certs/cloudflare-containers-ca.crt` for HTTPS interception to work. + +Supports glob patterns where `*` matches any sequence of characters. + +```js +const worker = this.ctx.exports.MyWorker({ props: {} }); + +// Match a specific hostname +this.ctx.container.interceptOutboundHttps("api.example.com", worker); + +// Match a hostname glob pattern +this.ctx.container.interceptOutboundHttps("*.example.com", worker); + +// Intercept all HTTPS traffic +this.ctx.container.interceptOutboundHttps("*", worker); +``` + +#### Parameters + +- `target` (string): A hostname or hostname glob pattern to match. Use `*` to intercept all HTTPS traffic. +- `worker` (WorkerEntrypoint): A `WorkerEntrypoint` instance to handle matching requests. + +#### Return values + +- None. + +## Related resources + +- [Containers APIs](/containers/api/) — compare direct runtime control with the `Container` class +- [Container class reference](/containers/api/container-class/) — use higher-level lifecycle helpers +- [Containers overview](/containers/) +- [Get started with Containers](/containers/get-started/) +- [SQLite storage API](/durable-objects/api/sqlite-storage-api/) — persist state across container restarts +- [Durable Objects](/durable-objects/) — the underlying platform that powers Containers diff --git a/src/content/docs/containers/api/index.mdx b/src/content/docs/containers/api/index.mdx new file mode 100644 index 00000000000..dc3c3b0169f --- /dev/null +++ b/src/content/docs/containers/api/index.mdx @@ -0,0 +1,106 @@ +--- +pcx_content_type: navigation +title: API +description: Choose between the Durable Object Container API and the higher-level Container class. +sidebar: + order: 6 +products: + - containers + - durable-objects +--- + +import { CardGrid, LinkTitleCard } from "~/components"; + +Containers provide two APIs for managing a container from a Durable Object. Both APIs address the same container runtime. + +For new applications, use the Durable Object Container API when you need direct lifecycle control. Use the `Container` class when you prefer built-in lifecycle helpers. + + + + + Start, stop, monitor, and connect to a container through `ctx.container`. + + + + Use a higher-level class built on Durable Objects, with routing, readiness + checks, lifecycle hooks, and scheduling. + + + + +## Choose an API + +### Durable Object Container API + +The Durable Object Container API exposes the container runtime through `ctx.container`. Choose it when you need direct control over startup, shutdown, networking, or resource usage. You can add readiness checks, custom request routing, or lifecycle policies when your application needs them. + +### Container class + +The `Container` class builds on Durable Objects and the runtime API. Choose it when you prefer built-in request proxying, readiness checks, lifecycle hooks, and scheduling. These helpers reduce application code, but some features use Durable Object storage and alarms. + +The following table compares both options: + +| Requirement | Durable Object Container API | `Container` class | +| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Start and stop a container | [`start()`](/containers/api/durable-object-container/#start), [`signal()`](/containers/api/durable-object-container/#signal), and [`destroy()`](/containers/api/durable-object-container/#destroy) | [`start()`](/containers/api/container-class/#start), [`stop()`](/containers/api/container-class/#stop), and [`destroy()`](/containers/api/container-class/#destroy) | +| Send and proxy traffic | [`getTcpPort(port).fetch()`](/containers/api/durable-object-container/#gettcpport) and [`getTcpPort(port).connect()`](/containers/api/durable-object-container/#gettcpport) | [`fetch()`](/containers/api/container-class/#fetch) and [`containerFetch()`](/containers/api/container-class/#containerfetch) | +| Execute another process | [`exec()`](/containers/api/durable-object-container/#exec) | [`ctx.container.exec()`](/containers/api/container-class/#execute-commands) | +| Check port readiness | Use [`getTcpPort()`](/containers/api/durable-object-container/#gettcpport) in application code | [`startAndWaitForPorts()`](/containers/api/container-class/#startandwaitforports) and [`waitForPort()`](/containers/api/container-class/#waitforport) | +| Handle concurrent starts | Coordinate calls to [`start()`](/containers/api/durable-object-container/#start) when needed | Handled by [`start()`](/containers/api/container-class/#start) and [`startAndWaitForPorts()`](/containers/api/container-class/#startandwaitforports) | +| Run lifecycle hooks | [`monitor()`](/containers/api/durable-object-container/#monitor) and application code | [`onStart()`](/containers/api/container-class/#onstart), [`onStop()`](/containers/api/container-class/#onstop), [`onError()`](/containers/api/container-class/#onerror), and [`onActivityExpired()`](/containers/api/container-class/#onactivityexpired) | +| Stop inactive containers | [`setInactivityTimeout()`](/containers/api/durable-object-container/#setinactivitytimeout) | [`sleepAfter`](/containers/api/container-class/#sleepafter) and [`onActivityExpired()`](/containers/api/container-class/#onactivityexpired) | +| Schedule callbacks | [`ctx.storage.setAlarm()`](/durable-objects/api/alarms/#setalarm) (Durable Object API) | [`schedule()`](/containers/api/container-class/#schedule) | + +Use the Durable Object Container API for latency-sensitive workloads or workloads that need a smaller storage footprint. + +## Use the Durable Object Container API + +The Durable Object Container API is available through `ctx.container` of the Durable Object. It exposes the container runtime without adding lifecycle policy. + +```ts +import { DurableObject } from "cloudflare:workers"; + +export class MyContainer extends DurableObject { + constructor(ctx: DurableObjectState, env: Env) { + super(ctx, env); + ctx.blockConcurrencyWhile(() => + ctx.container.setInactivityTimeout(10 * 60 * 1000), + ); + } + + async fetch(request: Request): Promise { + if (!this.ctx.container.running) { + this.ctx.container.start({ enableInternet: true }); + } + + return this.ctx.container.getTcpPort(8080).fetch(request); + } +} +``` + +The `running` property does not indicate port readiness. Check the required port before routing the first request if your process needs time to start. + +For all methods, refer to the [Durable Object Container API](/containers/api/durable-object-container/). + +## Use the Container class + +The [`Container` class](https://github.com/cloudflare/containers) extends `DurableObject`. It adds default routing, readiness checks, lifecycle hooks, activity tracking, and scheduled callbacks. + +```ts +import { Container } from "@cloudflare/containers"; + +export class MyContainer extends Container { + defaultPort = 8080; + sleepAfter = "10m"; +} +``` + +These helpers reduce application code. They also add lifecycle state and scheduled work to the Durable Object. For all properties and methods, refer to the [Container class API](/containers/api/container-class/). diff --git a/src/content/docs/containers/concepts/architecture.mdx b/src/content/docs/containers/concepts/architecture.mdx index 4b3c7b4da8a..631e67ff529 100644 --- a/src/content/docs/containers/concepts/architecture.mdx +++ b/src/content/docs/containers/concepts/architecture.mdx @@ -17,7 +17,38 @@ times when scaling up the number of concurrent container instances. Worker code goes live on deploy. Container instances update with a [rollout](/containers/configuration/rollouts/). Refer to [Deploy Containers](/containers/guides/deploy/). -## Lifecycle of a Request +## Container instance lifecycle + +```mermaid +flowchart LR + accTitle: Container instance lifecycle + accDescr: A Worker accesses a container through its Durable Object. The container moves from stopped to starting, running but not ready, ready for traffic, stopping, and stopped. + + Worker["Worker"] -->|Durable Object binding| DurableObject["Durable Object"] + DurableObject -->|ctx.container| Container + + subgraph Container["Container instance"] + direction TD + Stopped["Stopped"] + Starting["Starting
start() called"] + Running["Running
Not ready"] + Ready["Ready
Accepting traffic"] + Stopping["Stopping
Stop requested"] + StoppedAgain["Stopped"] + + Stopped --> Starting --> Running --> Ready --> Stopping --> StoppedAgain + end +``` + +A Container can only be accessed through its Durable Object. A Worker sends a request to the Durable Object, which accesses the Container through `ctx.container`. + +An inactivity timeout, `signal()`, `destroy()`, a rollout, or a process exit can stop the instance. If startup fails or the process exits early, the instance returns to the stopped state. + +The `ctx.container.running` property becomes `true` before the process is ready to accept traffic. Check port readiness before you send the first request. + +You can manage this lifecycle through the [Durable Object Container API](/containers/api/durable-object-container/) or the higher-level [`Container` class](/containers/api/container-class/). To compare both options, refer to [Containers APIs](/containers/api/). + +## Lifecycle of a request ### Client to Worker @@ -33,11 +64,9 @@ or UDP from an end-user, please [let us know](https://forms.gle/AGSq54VvUje6kmKu ### Worker to Durable Object -From the Worker, a request passes through a Durable Object instance (the [Container class](/containers/reference/container-class/) extends a Durable Object class). +From the Worker, a request passes through a Durable Object instance. You can extend `DurableObject` and use `ctx.container` directly, or extend the [`Container` class](/containers/api/container-class/). Each Durable Object instance is a globally routable isolate that can execute code and store state. This allows -developers to easily address and route to specific container instances (no matter where they are placed), -define and run hooks on container status changes, execute recurring checks on the instance, and store persistent -state associated with each instance. +developers to address and route to specific container instances, run code when a container exits, and store persistent state associated with each instance. ### Starting a Container @@ -91,9 +120,9 @@ should be built for the `linux/amd64` architecture, and should stay within ### Container shutdown -The Container class sets [`sleepAfter`](/containers/reference/container-class/#sleepafter) to 10 minutes by default. Its default [`onActivityExpired()`](/containers/reference/container-class/#onactivityexpired) implementation signals the container to stop after that period without activity. You can change the duration or override the hook. +With the Durable Object Container API, call [`setInactivityTimeout()`](/containers/api/durable-object-container/#setinactivitytimeout) to let the runtime stop an inactive container. You can also stop a container with [`signal()`](/containers/api/durable-object-container/#signal) or [`destroy()`](/containers/api/durable-object-container/#destroy). -You can stop a container instance yourself with [`stop()`](/containers/reference/container-class/#stop) or [`destroy()`](/containers/reference/container-class/#destroy). +The `Container` class sets [`sleepAfter`](/containers/api/container-class/#sleepafter) to 10 minutes by default. Its [`onActivityExpired()`](/containers/api/container-class/#onactivityexpired) implementation calls [`stop()`](/containers/api/container-class/#stop). You can change the duration or override the hook. When the platform is about to stop a container instance, it: @@ -103,14 +132,16 @@ When the platform is about to stop a container instance, it: Handle `SIGTERM` in your image if you need cleanup before exit. The same sequence runs when a [rollout](/containers/configuration/rollouts/) replaces a container instance with a new image. -### Lifecycle hooks +### Lifecycle events + +The Durable Object Container API provides [`monitor()`](/containers/api/durable-object-container/#monitor). Its promise resolves when the container exits and rejects when the container errors. -The [`Container` class](/containers/reference/container-class/) provides hooks that run Worker code when the container changes state: +The [`Container` class](/containers/api/container-class/) adds hooks that run Worker code when the container changes state: -- [`onStart()`](/containers/reference/container-class/#onstart) — Runs after the container has started. -- [`onStop()`](/containers/reference/container-class/#onstop) — Runs after the container process exits. Receives the exit code and reason for the stop. -- [`onActivityExpired()`](/containers/reference/container-class/#onactivityexpired) — Runs when the [`sleepAfter`](/containers/reference/container-class/#sleepafter) timer expires with no incoming requests. The default implementation calls `stop()` to shut down the container. You can use this to only stop the container on certain conditions. -- [`onError()`](/containers/reference/container-class/#onerror) — Runs when the container exits with an error. +- [`onStart()`](/containers/api/container-class/#onstart) — Runs after the container has started. +- [`onStop()`](/containers/api/container-class/#onstop) — Runs after the container process exits. Receives the exit code and reason for the stop. +- [`onActivityExpired()`](/containers/api/container-class/#onactivityexpired) — Runs when the [`sleepAfter`](/containers/api/container-class/#sleepafter) timer expires with no incoming requests. The default implementation calls `stop()` to shut down the container. You can use this to only stop the container on certain conditions. +- [`onError()`](/containers/api/container-class/#onerror) — Runs when the container exits with an error. Refer to the [status hooks example](/containers/examples/status-hooks/) for a full implementation. diff --git a/src/content/docs/containers/configuration/environment-variables.mdx b/src/content/docs/containers/configuration/environment-variables.mdx index 62612167fa4..ac71f31fa1c 100644 --- a/src/content/docs/containers/configuration/environment-variables.mdx +++ b/src/content/docs/containers/configuration/environment-variables.mdx @@ -3,7 +3,7 @@ pcx_content_type: reference title: Environment Variables description: Runtime and user-defined environment variables available inside Container instances. sidebar: - order: 2 + order: 3 products: - containers --- diff --git a/src/content/docs/containers/configuration/index.mdx b/src/content/docs/containers/configuration/index.mdx index a49102d100a..787c7a1766a 100644 --- a/src/content/docs/containers/configuration/index.mdx +++ b/src/content/docs/containers/configuration/index.mdx @@ -1,7 +1,7 @@ --- pcx_content_type: navigation title: Configuration -description: Configure Containers — connect them to Workers and bindings, set environment variables, tune scaling and routing, and manage rollouts. +description: Configure Containers in Wrangler, connect them to Workers and bindings, set environment variables, tune scaling and routing, and manage rollouts. sidebar: order: 5 group: diff --git a/src/content/docs/containers/guides/outbound-traffic.mdx b/src/content/docs/containers/configuration/outbound-traffic.mdx similarity index 99% rename from src/content/docs/containers/guides/outbound-traffic.mdx rename to src/content/docs/containers/configuration/outbound-traffic.mdx index fbe1ee0e430..c102fa967bc 100644 --- a/src/content/docs/containers/guides/outbound-traffic.mdx +++ b/src/content/docs/containers/configuration/outbound-traffic.mdx @@ -414,4 +414,4 @@ The `Container` class calls these methods automatically when you use the functio - [Connect to Workers bindings](/containers/configuration/workers-connections/) — Access KV, R2, Durable Objects, and other bindings from a container - [Control outbound traffic (Sandboxes)](/sandbox/guides/outbound-traffic/) — Sandbox SDK API for outbound handlers - [Environment variables and secrets](/containers/configuration/environment-variables/) — Configure secrets and environment variables -- [Durable Object interface](/durable-objects/api/container/) — Full `ctx.container` API reference +- [Durable Object Container API](/containers/api/durable-object-container/) — Full `ctx.container` API reference diff --git a/src/content/docs/containers/configuration/rollouts.mdx b/src/content/docs/containers/configuration/rollouts.mdx index d658eb17a03..cb9b73c551c 100644 --- a/src/content/docs/containers/configuration/rollouts.mdx +++ b/src/content/docs/containers/configuration/rollouts.mdx @@ -3,7 +3,7 @@ pcx_content_type: reference title: Rollouts description: How container instances update after a deploy, including step percentages, grace periods, and rollout modes. sidebar: - order: 4 + order: 5 products: - containers --- @@ -53,7 +53,7 @@ When the rollout selects a container instance to update: 2. **Signal stop.** The platform sends `SIGTERM` to the main process in the container so it can stop accepting new work and finish in-flight work. Handle `SIGTERM` in your image if that process needs cleanup before exit. 3. **Drain.** The process has up to 15 minutes to exit after `SIGTERM`. 4. **Force stop if needed.** If the process is still running after 15 minutes, the platform sends `SIGKILL`. -5. **After exit.** The Container class [`onStop`](/containers/reference/container-class/#onstop) hook can run in the Worker once the container process has exited. +5. **After exit.** The Container class [`onStop`](/containers/api/container-class/#onstop) hook can run in the Worker once the container process has exited. 6. **Start a new container instance** with the target image. Disk is [ephemeral](/containers/faq/#is-disk-persistent-what-happens-to-my-disk-when-my-container-sleeps) unless you store data outside the container filesystem. Each selected container instance follows this sequence on its own schedule. The fleet does not restart in a single moment. @@ -160,4 +160,4 @@ Use none when the deploy should not publish a new image or start a container ins - [Deploy Containers](/containers/guides/deploy/) - [Lifecycle of a Container](/containers/concepts/architecture/) - [Image management](/containers/guides/image-management/) -- [Containers configuration](/workers/wrangler/configuration/#containers) +- [Wrangler configuration](/containers/configuration/wrangler/) diff --git a/src/content/docs/containers/configuration/scaling-and-routing.mdx b/src/content/docs/containers/configuration/scaling-and-routing.mdx index 7306d9c20c9..c5f85f46f7c 100644 --- a/src/content/docs/containers/configuration/scaling-and-routing.mdx +++ b/src/content/docs/containers/configuration/scaling-and-routing.mdx @@ -3,7 +3,7 @@ pcx_content_type: reference title: Scaling and Routing description: Scale Container instances using explicit IDs or the getRandom helper for stateless load balancing. sidebar: - order: 3 + order: 4 products: - containers --- @@ -11,7 +11,7 @@ products: ## Scale container instances with explicit IDs :::note -This section uses helpers from the [Container class](/containers/reference/container-class/). +This section uses helpers from the [Container class](/containers/api/container-class/). ::: Today, Containers are scaled manually by getting containers with a unique ID, then diff --git a/src/content/docs/containers/configuration/workers-connections.mdx b/src/content/docs/containers/configuration/workers-connections.mdx index bd00691c72f..1aa35122871 100644 --- a/src/content/docs/containers/configuration/workers-connections.mdx +++ b/src/content/docs/containers/configuration/workers-connections.mdx @@ -2,13 +2,13 @@ title: Connect to Workers and Bindings pcx_content_type: concept sidebar: - order: 1 + order: 2 description: Access KV, R2, Durable Objects, and other bindings from a container. products: - containers --- -Containers can access [Workers bindings](/workers/runtime-apis/bindings/) — KV, R2, D1, Durable Objects, and others — through [outbound handlers](/containers/guides/outbound-traffic/#define-outbound-handlers). An outbound handler intercepts HTTP requests from the container and runs inside the Workers runtime, where all of your configured bindings are available. +Containers can access [Workers bindings](/workers/runtime-apis/bindings/) — KV, R2, D1, Durable Objects, and others — through [outbound handlers](/containers/configuration/outbound-traffic/#define-outbound-handlers). An outbound handler intercepts HTTP requests from the container and runs inside the Workers runtime, where all of your configured bindings are available. The container makes a plain HTTP request to a virtual hostname (for example, `http://my.kv/some-key`), and the outbound handler resolves it using the bound resource. No SDK or client library is required inside the container. @@ -57,6 +57,6 @@ The `ctx` argument exposes `containerId`, which lets you interact with the conta ## Related resources -- [Handle outbound traffic](/containers/guides/outbound-traffic/) — Block, allow, and intercept all outbound HTTP from a container +- [Handle outbound traffic](/containers/configuration/outbound-traffic/) — Block, allow, and intercept all outbound HTTP from a container - [Environment variables and secrets](/containers/configuration/environment-variables/) — Configure secrets and environment variables -- [Durable Object interface](/durable-objects/api/container/) — Full `ctx.container` API reference +- [Durable Object Container API](/containers/api/durable-object-container/) — Full `ctx.container` API reference diff --git a/src/content/docs/containers/configuration/wrangler.mdx b/src/content/docs/containers/configuration/wrangler.mdx new file mode 100644 index 00000000000..3b5597754c3 --- /dev/null +++ b/src/content/docs/containers/configuration/wrangler.mdx @@ -0,0 +1,73 @@ +--- +title: Wrangler configuration +description: Configure a Container, its Durable Object binding, and its migration in Wrangler. +pcx_content_type: configuration +sidebar: + order: 0 +products: + - containers + - durable-objects +--- + +import { WranglerConfig } from "~/components"; + +Define Containers in the Wrangler configuration file for your Worker. Each Container is associated with a Durable Object class, which provides access to the Container at runtime. + +## Minimal configuration + +A Container application requires a Container definition, a Durable Object binding, and a Durable Object migration: + + + +```jsonc +{ + "$schema": "./node_modules/wrangler/config-schema.json", + "name": "my-container-worker", + "main": "src/index.ts", + "compatibility_date": "$today", + "containers": [ + { + "class_name": "MyContainer", + "image": "./Dockerfile", + "max_instances": 10, + }, + ], + "durable_objects": { + "bindings": [ + { + "name": "MY_CONTAINER", + "class_name": "MyContainer", + }, + ], + }, + "migrations": [ + { + "tag": "v1", + "new_sqlite_classes": ["MyContainer"], + }, + ], +} +``` + + + +The configuration uses three sections: + +1. **`containers`** defines the container image and associates it with a Durable Object class through `class_name`. +2. **`durable_objects.bindings`** makes the Durable Object namespace available to Worker code. In this example, access it through `env.MY_CONTAINER`. +3. **`migrations`** creates the SQLite-backed Durable Object class. Use `new_sqlite_classes`, not `new_classes`, for a Container. + +The `class_name` in all three sections must match the exported Durable Object class in your Worker. + +## Container settings + +The `containers` entry can also configure the instance type, maximum number of running instances, image build, placement constraints, rollouts, and SSH access. + +For all available fields and values, refer to the [Containers Wrangler configuration reference](/workers/wrangler/configuration/#containers). + +## Next steps + +- [Deploy Containers](/containers/guides/deploy/) — Build the image and deploy the Worker. +- [Scaling and Routing](/containers/configuration/scaling-and-routing/) — Route requests and scale Container instances. +- [Rollouts](/containers/configuration/rollouts/) — Control how configuration changes reach running instances. +- [Image management](/containers/guides/image-management/) — Use local and remote container images. diff --git a/src/content/docs/containers/examples/container-backend.mdx b/src/content/docs/containers/examples/container-backend.mdx index fc458f7461a..e44a4b4d9c4 100644 --- a/src/content/docs/containers/examples/container-backend.mdx +++ b/src/content/docs/containers/examples/container-backend.mdx @@ -10,7 +10,7 @@ products: - containers --- -import { WranglerConfig, Details } from "~/components"; +import { WranglerConfig, Details, TabItem, Tabs, TypeScriptExample } from "~/components"; A common pattern is to serve a static frontend application (e.g., React, Vue, Svelte) using Static Assets, then pass backend requests to a containerized backend application. @@ -138,6 +138,68 @@ Your Worker needs to be able to both serve static assets and route requests to t In this case, we will pass requests to one of three container instances if the route starts with `/api`, and all other requests will be served as static assets. + + + + +```ts +import { DurableObject } from "cloudflare:workers"; + +const INSTANCE_COUNT = 3; + +interface Env { + ASSETS: Fetcher; + BACKEND: DurableObjectNamespace; +} + +export class Backend extends DurableObject { + constructor(ctx: DurableObjectState, env: Env) { + super(ctx, env); + + ctx.blockConcurrencyWhile(async () => { + const container = ctx.container!; + await container.setInactivityTimeout(2 * 60 * 60 * 1000); + + if (!container.running) { + container.start(); + } + + const port = container.getTcpPort(8080); + let lastError: unknown; + for (let attempt = 0; attempt < 50; attempt++) { + try { + await port.fetch("http://container/"); + return; + } catch (error) { + lastError = error; + await scheduler.wait(100); + } + } + throw lastError; + }); + } + + fetch(request: Request): Promise { + return this.ctx.container!.getTcpPort(8080).fetch(request); + } +} + +export default { + async fetch(request: Request, env: Env): Promise { + if (new URL(request.url).pathname.startsWith("/api")) { + const index = Math.floor(Math.random() * INSTANCE_COUNT); + return env.BACKEND.getByName(`instance-${index}`).fetch(request); + } + + return env.ASSETS.fetch(request); + }, +}; +``` + + + + + ```javascript import { Container, getRandom } from "@cloudflare/containers"; @@ -161,9 +223,11 @@ export default { }; ``` + + + :::note -This example uses `getRandom`, which randomly selects one of a fixed number of Container -instances for each request. +Both examples randomly select one of a fixed number of Container instances for each request. The `Container` class provides `getRandom()` as a helper. In the future, we will provide improved latency-aware load balancing and autoscaling. diff --git a/src/content/docs/containers/examples/cron.mdx b/src/content/docs/containers/examples/cron.mdx index beb5c71d036..9f06cadc5e2 100644 --- a/src/content/docs/containers/examples/cron.mdx +++ b/src/content/docs/containers/examples/cron.mdx @@ -10,7 +10,7 @@ products: - containers --- -import { WranglerConfig } from "~/components"; +import { TabItem, Tabs, TypeScriptExample, WranglerConfig } from "~/components"; To launch a container on a schedule, you can use a Workers [Cron Trigger](/workers/configuration/cron-triggers/). @@ -54,10 +54,81 @@ Use a cron expression in your Wrangler config to specify the schedule: -Then in your Worker, call your Container from the "scheduled" handler: +Then call the Container from the `scheduled()` handler in the Worker. The raw API example expects the Container to expose a `POST /run` endpoint that starts the scheduled task. + + + + ```ts -import { Container, getContainer } from '@cloudflare/containers'; +import { DurableObject } from "cloudflare:workers"; + +interface Env { + CRON_CONTAINER: DurableObjectNamespace; +} + +export class CronContainer extends DurableObject { + private currentRun: Promise | undefined; + + run(startTime: string): Promise { + this.currentRun ??= this.runOnce(startTime).finally(() => { + this.currentRun = undefined; + }); + return this.currentRun; + } + + private async runOnce(startTime: string): Promise { + const container = this.ctx.container!; + await container.setInactivityTimeout(10_000); + + if (!container.running) { + container.start(); + } + + const port = container.getTcpPort(8080); + let lastError: unknown; + for (let attempt = 0; attempt < 50; attempt++) { + try { + await port.fetch("http://container/"); + lastError = undefined; + break; + } catch (error) { + lastError = error; + await scheduler.wait(100); + } + } + if (lastError) { + throw lastError; + } + + const response = await port.fetch("http://container/run", { + method: "POST", + body: JSON.stringify({ startTime }), + headers: { "content-type": "application/json" }, + }); + if (!response.ok) { + throw new Error(`Container returned ${response.status}`); + } + } +} + +export default { + async fetch(): Promise { + return new Response("This Worker runs a scheduled Container task."); + }, + + async scheduled(_controller: ScheduledController, env: Env): Promise { + await env.CRON_CONTAINER.getByName("cron").run(new Date().toISOString()); + }, +}; +``` + + + + + +```ts +import { Container, getContainer } from "@cloudflare/containers"; export class CronContainer extends Container { sleepAfter = '10s'; @@ -72,17 +143,20 @@ export class CronContainer extends Container { } export default { - async fetch(): Promise { - return new Response("This Worker runs a cron job to execute a container on a schedule."); - }, - - async scheduled(_controller: any, env: { CRON_CONTAINER: DurableObjectNamespace }) { - let container = getContainer(env.CRON_CONTAINER); - await container.start({ - envVars: { + async fetch(): Promise { + return new Response("This Worker runs a cron job to execute a container on a schedule."); + }, + + async scheduled(_controller: ScheduledController, env: { CRON_CONTAINER: DurableObjectNamespace }) { + const container = getContainer(env.CRON_CONTAINER); + await container.start({ + envVars: { MESSAGE: "Start Time: " + new Date().toISOString(), - } - }) - }, + }, + }); + }, }; ``` + + + diff --git a/src/content/docs/containers/examples/durable-object-interface.mdx b/src/content/docs/containers/examples/durable-object-interface.mdx deleted file mode 100644 index f957451fa12..00000000000 --- a/src/content/docs/containers/examples/durable-object-interface.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- - -summary: Various examples calling Containers directly from Durable Objects -pcx_content_type: example -title: Using Durable Objects Directly -external_link: https://github.com/cloudflare/containers-demos -sidebar: - order: 10 -description: Various examples calling Containers directly from Durable Objects -reviewed: 2025-06-24 -products: - - containers ---- diff --git a/src/content/docs/containers/examples/env-vars-and-secrets.mdx b/src/content/docs/containers/examples/env-vars-and-secrets.mdx index 00d3117d36b..0fc5aa497ae 100644 --- a/src/content/docs/containers/examples/env-vars-and-secrets.mdx +++ b/src/content/docs/containers/examples/env-vars-and-secrets.mdx @@ -10,10 +10,9 @@ products: - containers --- -import { WranglerConfig, PackageManagers } from "~/components"; +import { PackageManagers, TabItem, Tabs, TypeScriptExample, WranglerConfig } from "~/components"; -Environment variables can be passed into a Container using the `envVars` field -in the [`Container`](/containers/reference/container-class/) class, or by setting manually when the Container starts. +Environment variables can be passed when the Durable Object Container API starts a Container, or through the `envVars` field on the [`Container`](/containers/api/container-class/) class. Secrets can be passed into a Container by using [Worker Secrets](/workers/configuration/secrets/) or the [Secret Store](/secrets-store/integrations/workers/), then passing them into the Container @@ -82,7 +81,8 @@ in Wrangler configuration. { "name": "my-container-worker", "vars": { - "ENV_VAR": "my-env-var" + "ENV_VAR": "my-env-var", + "CONTAINER_IMAGE": "registry.cloudflare.com//my-container:latest" }, "secrets_store_secrets": [ { @@ -109,13 +109,52 @@ added to `env`. Also note that we did not configure anything specific for environment variables, secrets, or KV values in the _container-related_ portion of the Wrangler configuration file. -## Using `envVars` on the Container class +## Set environment variables for every instance -Now, let's pass the env vars and secrets to our container using the `envVars` field in the `Container` class: +Pass synchronous Worker variables and secrets when the Container starts. When using the raw API with startup options, provide a deployed image reference. + + + + + +```ts +import { DurableObject } from "cloudflare:workers"; + +interface Env { + CONTAINER_IMAGE: string; + ENV_VAR: string; + WORKER_SECRET: string; +} + +export class MyContainer extends DurableObject { + constructor(ctx: DurableObjectState, env: Env) { + super(ctx, env); + + ctx.blockConcurrencyWhile(async () => { + if (!ctx.container!.running) { + ctx.container!.start({ + image: env.CONTAINER_IMAGE, + enableInternet: true, + env: { + ENV_VAR: env.ENV_VAR, + WORKER_SECRET: env.WORKER_SECRET, + }, + }); + } + }); + } +} +``` + + + + ```js // https://developers.cloudflare.com/workers/runtime-apis/bindings/#importing-env-as-a-global import { env } from "cloudflare:workers"; +import { Container } from "@cloudflare/containers"; + export class MyContainer extends Container { defaultPort = 8080; sleepAfter = "10s"; @@ -127,6 +166,9 @@ export class MyContainer extends Container { } ``` + + + Every instance of this `Container` will now have these variables and secrets set as environment variables when it launches. @@ -134,7 +176,77 @@ set as environment variables when it launches. But what if you want to set environment variables on a per-instance basis? -In this case, use the `startAndWaitForPorts()` method to pass in environment variables for each instance. +Pass the values when starting each instance. The raw API example defines a `launch()` RPC method on the Durable Object. The class version uses `startAndWaitForPorts()`. + + + + + +```ts +import { DurableObject } from "cloudflare:workers"; + +interface Env { + CONTAINER_IMAGE: string; + DEMO_KV: KVNamespace; + ENV_VAR: string; + MY_CONTAINER: DurableObjectNamespace; + SECRET_STORE: SecretsStoreSecret; + WORKER_SECRET: string; +} + +export class MyContainer extends DurableObject { + launch(image: string, env: Record): void { + if (this.ctx.container!.running) { + throw new Error("Container is already running"); + } + this.ctx.container!.start({ image, enableInternet: true, env }); + } +} + +function required(value: string | null, name: string): string { + if (value === null) { + throw new Error(`${name} was not found`); + } + return value; +} + +export default { + async fetch(request: Request, env: Env): Promise { + if (new URL(request.url).pathname !== "/launch-instances") { + return new Response("Not found", { status: 404 }); + } + + const secretStoreSecret = await env.SECRET_STORE.get(); + const kvValue = required(await env.DEMO_KV.get("KV_VALUE"), "KV_VALUE"); + const instanceConfig = required( + await env.DEMO_KV.get("instance-bar-config"), + "instance-bar-config", + ); + + await Promise.all([ + env.MY_CONTAINER.getByName("foo").launch(env.CONTAINER_IMAGE, { + ENV_VAR: `${env.ENV_VAR}foo`, + WORKER_SECRET: env.WORKER_SECRET, + SECRET_STORE_SECRET: secretStoreSecret, + KV_VALUE: kvValue, + }), + env.MY_CONTAINER.getByName("bar").launch(env.CONTAINER_IMAGE, { + ENV_VAR: `${env.ENV_VAR}bar`, + WORKER_SECRET: env.WORKER_SECRET, + SECRET_STORE_SECRET: secretStoreSecret, + KV_VALUE: kvValue, + INSTANCE_CONFIG: instanceConfig, + }), + ]); + + return new Response("Container instances launched"); + }, +}; +``` + + + + ```js export class MyContainer extends Container { @@ -181,6 +293,9 @@ export default { }; ``` + + + ## Reading KV values in containers KV values are particularly useful for configuration data that changes infrequently but needs to be accessible to your containers. Since KV operations are asynchronous, you must read the values at runtime when starting containers. @@ -189,6 +304,45 @@ Here are common patterns for using KV with containers: ### Configuration data + + + + +```ts +export default { + async fetch(request: Request, env: Env): Promise { + if (new URL(request.url).pathname !== "/configure-container") { + return new Response("Not found", { status: 404 }); + } + + const config = await env.DEMO_KV.get("container-config", "json"); + const apiEndpoint = required( + await env.DEMO_KV.get("api-endpoint"), + "api-endpoint", + ); + const deploymentEnv = required( + await env.DEMO_KV.get("deployment-env"), + "deployment-env", + ); + + await env.MY_CONTAINER.getByName("configured").launch( + env.CONTAINER_IMAGE, + { + CONFIG_JSON: JSON.stringify(config), + API_ENDPOINT: apiEndpoint, + DEPLOYMENT_ENV: deploymentEnv, + }, + ); + + return new Response("Container configured and launched"); + }, +}; +``` + + + + + ```js export default { async fetch(request, env) { @@ -215,8 +369,54 @@ export default { }; ``` + + + ### Feature flags + + + + +```ts +export default { + async fetch(request: Request, env: Env): Promise { + if (new URL(request.url).pathname !== "/launch-with-features") { + return new Response("Not found", { status: 404 }); + } + + const featureFlags = { + ENABLE_FEATURE_A: required( + await env.DEMO_KV.get("feature-a-enabled"), + "feature-a-enabled", + ), + ENABLE_FEATURE_B: required( + await env.DEMO_KV.get("feature-b-enabled"), + "feature-b-enabled", + ), + DEBUG_MODE: required( + await env.DEMO_KV.get("debug-enabled"), + "debug-enabled", + ), + }; + + await env.MY_CONTAINER.getByName("features").launch( + env.CONTAINER_IMAGE, + { + ...featureFlags, + CONTAINER_VERSION: "1.2.3", + }, + ); + + return new Response("Container launched with feature flags"); + }, +}; +``` + + + + + ```js export default { async fetch(request, env) { @@ -245,6 +445,9 @@ export default { }; ``` + + + ## Build-time environment variables Finally, you can also set build-time environment variables that are only available when building the container image via the `image_vars` field in the Wrangler configuration. diff --git a/src/content/docs/containers/examples/index.mdx b/src/content/docs/containers/examples/index.mdx index 532425ee67a..337286b2dd3 100644 --- a/src/content/docs/containers/examples/index.mdx +++ b/src/content/docs/containers/examples/index.mdx @@ -3,7 +3,7 @@ pcx_content_type: navigation title: Examples description: Code examples showing how to use Containers with Workers for stateless routing, cron jobs, WebSockets, and more. sidebar: - order: 6 + order: 7 group: hideIndex: true products: diff --git a/src/content/docs/containers/examples/r2-fuse-mount.mdx b/src/content/docs/containers/examples/r2-fuse-mount.mdx index 90901640222..c179f5f39d8 100644 --- a/src/content/docs/containers/examples/r2-fuse-mount.mdx +++ b/src/content/docs/containers/examples/r2-fuse-mount.mdx @@ -11,7 +11,7 @@ products: - r2 --- -import { Details, TypeScriptExample } from "~/components"; +import { Details, TabItem, Tabs, TypeScriptExample } from "~/components"; FUSE (Filesystem in Userspace) allows you to mount [R2 buckets](/r2/) as filesystems within Containers. Applications can then interact with R2 using standard filesystem operations rather than object storage APIs. @@ -81,33 +81,95 @@ The startup script creates a mount point, starts tigrisfs in the background to m ### Passing credentials to the container -Your Container needs [R2 credentials](/r2/api/tokens/) and configuration passed as environment variables. Store credentials as [Worker secrets](/workers/configuration/secrets/), then pass them through the `envVars` property: +Your Container needs [R2 credentials](/r2/api/tokens/) and configuration passed as environment variables. Store credentials as [Worker secrets](/workers/configuration/secrets/), then pass them when the Container starts. + + + + + +```ts +import { DurableObject } from "cloudflare:workers"; + +interface Env { + FUSE_DEMO: DurableObjectNamespace; + FUSE_IMAGE: string; + AWS_ACCESS_KEY_ID: string; + AWS_SECRET_ACCESS_KEY: string; + R2_BUCKET_NAME: string; + R2_ACCOUNT_ID: string; +} + +export class FUSEDemo extends DurableObject { + private currentRun: Promise | undefined; + + run(): Promise { + this.currentRun ??= this.runOnce().finally(() => { + this.currentRun = undefined; + }); + return this.currentRun; + } + + private async runOnce(): Promise { + const container = this.ctx.container!; + if (container.running) { + throw new Error("Container is already running"); + } + + container.start({ + image: this.env.FUSE_IMAGE, + enableInternet: true, + env: { + AWS_ACCESS_KEY_ID: this.env.AWS_ACCESS_KEY_ID, + AWS_SECRET_ACCESS_KEY: this.env.AWS_SECRET_ACCESS_KEY, + R2_BUCKET_NAME: this.env.R2_BUCKET_NAME, + R2_ACCOUNT_ID: this.env.R2_ACCOUNT_ID, + }, + }); + + await container.monitor(); + } +} + +export default { + async fetch(_request: Request, env: Env): Promise { + await env.FUSE_DEMO.getByName("default").run(); + return new Response("FUSE task completed"); + }, +}; +``` + + + + ```ts -import { Container, getContainer } from "@cloudflare/containers"; +import { Container } from "@cloudflare/containers"; interface Env { - FUSEDemo: DurableObjectNamespace; - AWS_ACCESS_KEY_ID: string; - AWS_SECRET_ACCESS_KEY: string; - R2_BUCKET_NAME: string; - R2_ACCOUNT_ID: string; + FUSE_DEMO: DurableObjectNamespace; + AWS_ACCESS_KEY_ID: string; + AWS_SECRET_ACCESS_KEY: string; + R2_BUCKET_NAME: string; + R2_ACCOUNT_ID: string; } export class FUSEDemo extends Container { - defaultPort = 8080; - sleepAfter = "10m"; - envVars = { - AWS_ACCESS_KEY_ID: this.env.AWS_ACCESS_KEY_ID, - AWS_SECRET_ACCESS_KEY: this.env.AWS_SECRET_ACCESS_KEY, - R2_BUCKET_NAME: this.env.R2_BUCKET_NAME, - R2_ACCOUNT_ID: this.env.R2_ACCOUNT_ID, - }; + defaultPort = 8080; + sleepAfter = "10m"; + envVars = { + AWS_ACCESS_KEY_ID: this.env.AWS_ACCESS_KEY_ID, + AWS_SECRET_ACCESS_KEY: this.env.AWS_SECRET_ACCESS_KEY, + R2_BUCKET_NAME: this.env.R2_BUCKET_NAME, + R2_ACCOUNT_ID: this.env.R2_ACCOUNT_ID, + }; } ``` + + + The `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` should be stored as secrets, while `R2_BUCKET_NAME` and `R2_ACCOUNT_ID` can be configured as variables in your `wrangler.jsonc`: :::note[Creating your R2 AWS API keys] @@ -116,8 +178,9 @@ To get your `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`, [head to your R2 da ```json { "vars": { - "R2_BUCKET_NAME": "my-bucket", - "R2_ACCOUNT_ID": "your-account-id" + "FUSE_IMAGE": "registry.cloudflare.com//r2-fuse:latest", + "R2_BUCKET_NAME": "my-bucket", + "R2_ACCOUNT_ID": "your-account-id" } } ``` diff --git a/src/content/docs/containers/examples/stateless.mdx b/src/content/docs/containers/examples/stateless.mdx index 8cf6275a0df..37c1fc4e6d2 100644 --- a/src/content/docs/containers/examples/stateless.mdx +++ b/src/content/docs/containers/examples/stateless.mdx @@ -10,7 +10,66 @@ products: - containers --- -To simply proxy requests to one of multiple instances of a container, you can use the `getRandom` function: +import { TabItem, Tabs, TypeScriptExample } from "~/components"; + +To proxy requests across a fixed number of Container instances, select an instance name and forward the request through its Durable Object. + + + + + +```ts +import { DurableObject } from "cloudflare:workers"; + +const INSTANCE_COUNT = 3; + +interface Env { + BACKEND: DurableObjectNamespace; +} + +export class Backend extends DurableObject { + constructor(ctx: DurableObjectState, env: Env) { + super(ctx, env); + + ctx.blockConcurrencyWhile(async () => { + const container = ctx.container!; + await container.setInactivityTimeout(2 * 60 * 60 * 1000); + + if (!container.running) { + container.start(); + } + + const port = container.getTcpPort(8080); + let lastError: unknown; + for (let attempt = 0; attempt < 50; attempt++) { + try { + await port.fetch("http://container/"); + return; + } catch (error) { + lastError = error; + await scheduler.wait(100); + } + } + throw lastError; + }); + } + + fetch(request: Request): Promise { + return this.ctx.container!.getTcpPort(8080).fetch(request); + } +} + +export default { + async fetch(request: Request, env: Env): Promise { + const index = Math.floor(Math.random() * INSTANCE_COUNT); + return env.BACKEND.getByName(`instance-${index}`).fetch(request); + }, +}; +``` + + + + ```ts import { Container, getRandom } from "@cloudflare/containers"; @@ -30,9 +89,11 @@ export default { }; ``` + + + :::note -This example uses `getRandom`, which randomly selects one of a fixed number of Container -instances for each request. +Both examples randomly select one of a fixed number of Container instances for each request. The `Container` class provides `getRandom()` as a helper. In the future, we will provide improved latency-aware load balancing and autoscaling. diff --git a/src/content/docs/containers/examples/status-hooks.mdx b/src/content/docs/containers/examples/status-hooks.mdx index 1a0e219a1e5..e8b59689eea 100644 --- a/src/content/docs/containers/examples/status-hooks.mdx +++ b/src/content/docs/containers/examples/status-hooks.mdx @@ -11,8 +11,76 @@ products: - workers --- -When a Container starts, stops, becomes idle, and errors, it can trigger code execution in a Worker -that has defined status hooks on the `Container` class. Refer to the [Container class lifecycle hooks](/containers/reference/container-class/#lifecycle-hooks) for more details. +import { TabItem, Tabs, TypeScriptExample } from "~/components"; + +Use `monitor()` with the Durable Object Container API to run code after the Container exits or errors. The `Container` class adds named lifecycle hooks and an inactivity callback. + + + + + +```ts +import { DurableObject } from "cloudflare:workers"; + +interface Env {} + +export class MyContainer extends DurableObject { + private ready: Promise | undefined; + + constructor(ctx: DurableObjectState, env: Env) { + super(ctx, env); + ctx.blockConcurrencyWhile(() => + ctx.container!.setInactivityTimeout(5 * 60 * 1000), + ); + } + + async fetch(request: Request): Promise { + const container = this.ctx.container!; + if (!container.running) { + this.ready = undefined; + } + this.ready ??= this.startAndMonitor().catch((error: unknown) => { + this.ready = undefined; + throw error; + }); + await this.ready; + + return container.getTcpPort(4000).fetch(request); + } + + private async startAndMonitor(): Promise { + const container = this.ctx.container!; + if (!container.running) { + container.start(); + } + + this.ctx.waitUntil( + container + .monitor() + .then(() => console.log("Container stopped")) + .catch((error: unknown) => console.error("Container error:", error)), + ); + + const port = container.getTcpPort(4000); + let lastError: unknown; + for (let attempt = 0; attempt < 50; attempt++) { + try { + await port.fetch("http://container/"); + console.log("Container successfully started"); + return; + } catch (error) { + lastError = error; + await scheduler.wait(100); + } + } + throw lastError; + } +} +``` + + + + ```ts import { Container } from "@cloudflare/containers"; @@ -45,3 +113,8 @@ export class MyContainer extends Container { } } ``` + + + + +The `monitor()` promise in the raw API does not include an exit code or stop reason. The `setInactivityTimeout()` method does not invoke a callback when the timeout expires. Use the [`Container` class lifecycle hooks](/containers/api/container-class/#lifecycle-hooks) when you need those higher-level events. diff --git a/src/content/docs/containers/examples/websocket.mdx b/src/content/docs/containers/examples/websocket.mdx index 66a5968d448..b8a92a1955f 100644 --- a/src/content/docs/containers/examples/websocket.mdx +++ b/src/content/docs/containers/examples/websocket.mdx @@ -10,8 +10,63 @@ products: - containers --- -WebSocket requests are automatically forwarded to a container using the default `fetch` -method on the `Container` class: +import { TabItem, Tabs, TypeScriptExample } from "~/components"; + +Forward an incoming WebSocket upgrade request through the Durable Object to the listening port on the Container. + + + + + +```ts +import { DurableObject } from "cloudflare:workers"; + +interface Env { + MY_CONTAINER: DurableObjectNamespace; +} + +export class MyContainer extends DurableObject { + constructor(ctx: DurableObjectState, env: Env) { + super(ctx, env); + + ctx.blockConcurrencyWhile(async () => { + const container = ctx.container!; + await container.setInactivityTimeout(2 * 60 * 1000); + + if (!container.running) { + container.start(); + } + + const port = container.getTcpPort(8080); + let lastError: unknown; + for (let attempt = 0; attempt < 50; attempt++) { + try { + await port.fetch("http://container/"); + return; + } catch (error) { + lastError = error; + await scheduler.wait(100); + } + } + throw lastError; + }); + } + + fetch(request: Request): Promise { + return this.ctx.container!.getTcpPort(8080).fetch(request); + } +} + +export default { + fetch(request: Request, env: Env): Promise { + return env.MY_CONTAINER.getByName("default").fetch(request); + }, +}; +``` + + + + ```js import { Container, getContainer } from "@cloudflare/containers"; @@ -29,5 +84,8 @@ export default { }; ``` + + + View a full example in the [Container class repository](https://github.com/cloudflare/containers/tree/main/examples/websocket). {/* TODO: Add more advanced examples - like kicking off a WS request then passing messages to container from the WS */} diff --git a/src/content/docs/containers/faq.mdx b/src/content/docs/containers/faq.mdx index 77b127711ec..94ddc8be3f1 100644 --- a/src/content/docs/containers/faq.mdx +++ b/src/content/docs/containers/faq.mdx @@ -123,7 +123,9 @@ Containers do not use swap memory. ## How long can instances run for? What happens when a host server is shut down? -Cloudflare does not stop a container instance after a fixed maximum runtime. The Container class sets [`sleepAfter`](/containers/reference/container-class/#sleepafter) to 10 minutes by default, and its default [`onActivityExpired()`](/containers/reference/container-class/#onactivityexpired) implementation signals the container to stop after that period without activity. You can change the duration or override the hook. Even if your hook keeps the instance running, another platform event can stop it. One of those cases is a host server restart, which happens on an irregular cadence. Cloudflare does not guarantee that any container instance will run for any set period of time. +Cloudflare does not stop a container instance after a fixed maximum runtime. With the Durable Object Container API, call [`setInactivityTimeout()`](/containers/api/durable-object-container/#setinactivitytimeout) to stop an inactive container. The `Container` class sets [`sleepAfter`](/containers/api/container-class/#sleepafter) to 10 minutes by default. Its [`onActivityExpired()`](/containers/api/container-class/#onactivityexpired) implementation signals the container to stop after that period without activity. You can change the duration or override the hook. + +Another platform event can stop an active container. For example, a host server restart happens on an irregular cadence. Cloudflare does not guarantee that any container instance will run for a set period. When the platform is about to stop a container instance (including before a host moves work off a server), it: @@ -175,4 +177,4 @@ For a complete working example, see the [Docker-in-Docker Containers example](ht ## How do I allow or disallow egress from my container? -Refer to [Handle outbound traffic](/containers/guides/outbound-traffic/) for how to control outbound traffic and internet access. +Refer to [Handle outbound traffic](/containers/configuration/outbound-traffic/) for how to control outbound traffic and internet access. diff --git a/src/content/docs/containers/get-started/index.mdx b/src/content/docs/containers/get-started/index.mdx index b8c6fdeeae9..c33f6ed0d61 100644 --- a/src/content/docs/containers/get-started/index.mdx +++ b/src/content/docs/containers/get-started/index.mdx @@ -15,6 +15,8 @@ In this example, each container runs a small webserver written in Go. This example Worker should give you a sense for simple Container use, and provide a starting point for more complex use cases. +This guide uses the higher-level `Container` class. You can also manage containers directly through `ctx.container`. To compare both options, refer to [Containers APIs](/containers/api/). + ## Prerequisites ### Ensure Docker is running locally @@ -72,7 +74,7 @@ Now that you've deployed your first container, let's explain what is happening i ### Configuration -Your [Wrangler configuration file](/workers/wrangler/configuration/) defines the configuration for both your Worker and your container: +Your [Wrangler configuration file](/containers/configuration/wrangler/) defines the configuration for both your Worker and your container: @@ -171,7 +173,7 @@ This defines basic configuration for the container: The `Container` class itself extends [`DurableObject`](/durable-objects/), so your subclass has access to the full Durable Object API. The Durable Object handles routing, lifecycle, and persistent state, while the container process runs your image inside a Linux VM. This means you can use [`this.ctx.storage`](/durable-objects/api/sqlite-storage-api/) to persist data that survives container restarts and resides close to the container itself. -Refer to the [Container class reference](/containers/reference/container-class/) and the [low-level Durable Object container API](/durable-objects/api/container/) for more details. +For all properties and methods, refer to the [Container class API](/containers/api/container-class/). #### Routing to Containers diff --git a/src/content/docs/containers/guides/execute-commands.mdx b/src/content/docs/containers/guides/execute-commands.mdx index c5160e3cba6..7b05288a936 100644 --- a/src/content/docs/containers/guides/execute-commands.mdx +++ b/src/content/docs/containers/guides/execute-commands.mdx @@ -11,7 +11,7 @@ reviewed: 2026-06-18 import { TypeScriptExample } from "~/components"; -Use `exec()` to start another process inside a running [Container](/containers/reference/container-class/). The examples call `this.ctx.container.exec()` inside a class extending `Container` from `@cloudflare/containers`. +Use `exec()` to start another process inside a running [Container](/containers/api/container-class/). The examples call `this.ctx.container.exec()` inside a class extending `Container` from `@cloudflare/containers`. `exec()` does not start a stopped Container. In remote procedure call (RPC) methods, check `this.ctx.container.running` and call `await this.start()` when needed. You can also use the `onStart()` hook to run any series of commands whenever the Container starts. @@ -490,4 +490,4 @@ export class MyContainer extends Container { ``` -For all fields and return types, refer to the [`exec()` API contract](/durable-objects/api/container/#exec). +For all fields and return types, refer to the [`exec()` API contract](/containers/api/durable-object-container/#exec). diff --git a/src/content/docs/containers/index.mdx b/src/content/docs/containers/index.mdx index a2cf34aff99..4c875d13b88 100644 --- a/src/content/docs/containers/index.mdx +++ b/src/content/docs/containers/index.mdx @@ -170,11 +170,7 @@ Ship from your machine or Workers Builds, and confirm the deploy. How a container is scheduled, started, routed, and shut down. - + Instance counts, image size, and other platform limits. @@ -186,12 +182,8 @@ Ship from your machine or Workers Builds, and confirm the deploy. CLI commands for images and containers. - - Start, stop, and talk to the container process from a Durable Object. + + Choose between direct runtime control and higher-level lifecycle helpers. @@ -203,7 +195,8 @@ Ship from your machine or Workers Builds, and confirm the deploy. href="https://discord.cloudflare.com" icon="discord" > - Ask questions, show what you are building, and talk with other Containers developers. + Ask questions, show what you are building, and talk with other Containers + developers. diff --git a/src/content/docs/containers/platform/index.mdx b/src/content/docs/containers/platform/index.mdx index 3a596df5460..581b8f1d3c1 100644 --- a/src/content/docs/containers/platform/index.mdx +++ b/src/content/docs/containers/platform/index.mdx @@ -3,7 +3,7 @@ pcx_content_type: navigation title: Platform description: Product-wide information for Containers, including pricing and limits. sidebar: - order: 8 + order: 9 group: hideIndex: true products: diff --git a/src/content/docs/containers/reference/durable-object-methods.mdx b/src/content/docs/containers/reference/durable-object-methods.mdx deleted file mode 100644 index 8889c8cc6c5..00000000000 --- a/src/content/docs/containers/reference/durable-object-methods.mdx +++ /dev/null @@ -1,11 +0,0 @@ ---- -pcx_content_type: navigation -title: Durable Object Interface -description: API reference for the low-level Durable Object methods that control Container instances. -external_link: /durable-objects/api/container/ -sidebar: - order: 2 -products: - - containers - - durable-objects ---- diff --git a/src/content/docs/containers/reference/index.mdx b/src/content/docs/containers/reference/index.mdx index 866ffe34560..8df5a63ad4a 100644 --- a/src/content/docs/containers/reference/index.mdx +++ b/src/content/docs/containers/reference/index.mdx @@ -1,9 +1,9 @@ --- pcx_content_type: navigation title: Reference -description: Lookup details for the Containers platform, including the Container class, the Durable Object interface, and Wrangler configuration and commands. +description: Lookup Wrangler configuration and commands for Containers. sidebar: - order: 7 + order: 8 group: hideIndex: true products: diff --git a/src/content/docs/durable-objects/api/container.mdx b/src/content/docs/durable-objects/api/container.mdx index e335a576411..cfc46405ca5 100644 --- a/src/content/docs/durable-objects/api/container.mdx +++ b/src/content/docs/durable-objects/api/container.mdx @@ -1,345 +1,11 @@ --- -title: Durable Object Container -description: Access and manage containers associated with a Durable Object, including start, stop, and interaction methods. -pcx_content_type: concept +pcx_content_type: navigation +title: Durable Object Container API +description: Manage a container directly through its Durable Object. +external_link: /containers/api/durable-object-container/ sidebar: - order: 1 + order: 7.5 products: + - containers - durable-objects --- - -import { - Render, - Tabs, - TabItem, - GlossaryTooltip, - Type, - MetaInfo, - TypeScriptExample, -} from "~/components"; - -## Description - -Each [container](/containers/) is managed by a Durable Object. The [`Container` class](/containers/reference/container-class/) from `@cloudflare/containers` extends `DurableObject` and handles lifecycle management, port readiness, and sleep timeouts for you. The Durable Object manages routing, persistent state, and lifecycle hooks, while the container process runs your image inside a Linux VM. - -The low-level API documented on this page is available on `this.ctx.container` inside any Durable Object class that has a container binding. Use it when you need direct control over the container process or cannot use the `Container` class. - -Because the `Container` class extends `DurableObject`, you also have access to [SQLite storage](/durable-objects/api/sqlite-storage-api/) via `this.ctx.storage`, [alarms](/durable-objects/api/alarms/), and all other Durable Object APIs. - - -```ts -export class MyDurableObject extends DurableObject { - constructor(ctx: DurableObjectState, env: Env) { - super(ctx, env); - - // boot the container when starting the DO - this.ctx.blockConcurrencyWhile(async () => { - this.ctx.container.start(); - }); - } - -} - -```` - - - -## Attributes - -### `running` - -`running` returns `true` if the container is currently running. It does not ensure that the container has fully started and ready to accept requests. - -```js - this.ctx.container.running; -```` - -## Methods - -### `start` - -`start` boots a container. This method does not block until the container is fully started. -You may want to confirm the container is ready to accept requests before using it. - -```js -this.ctx.container.start({ - env: { - FOO: "bar", - }, - enableInternet: false, - entrypoint: ["node", "server.js"], -}); -``` - -#### Parameters - -- `options` (optional): An object with the following properties: - - `env`: An object containing environment variables to pass to the container. This is useful for passing configuration values or secrets to the container. - - `entrypoint`: An array of strings representing the command to run in the container. - - `enableInternet`: A boolean indicating whether to enable internet access for the container. - -#### Return values - -- None. - -### `exec` - -`exec` starts another process inside an already-running Container. It does not start a stopped Container. - -The following example calls `this.ctx.container.exec()` inside a class extending `Container` from `@cloudflare/containers`. In RPC methods, check `this.ctx.container.running` and call `await this.start()` when needed. You can also use the `onStart()` hook to run any series of commands whenever the Container starts. - -```ts -exec( - cmd: string[], - options?: ContainerExecOptions, -): Promise -``` - -The `exec` operation starts the executable directly with the provided arguments. It does not start a shell or interpret pipes, redirects, expansion, or other shell syntax. Invoke Bash explicitly with `["bash", "-lc", ""]` when Bash exists in the image. Use `["sh", "-c", ""]` for images with only a Portable Operating System Interface (POSIX) shell. - -The following RPC method starts the Container before executing a command: - - -```ts -import { Container } from "@cloudflare/containers"; - -export class MyContainer extends Container { - async runCommand() { - if (!this.ctx.container.running) { - await this.start(); - } - - const process = await this.ctx.container.exec(["node", "--version"]); - const output = await process.output(); - - return { - pid: process.pid, - exitCode: output.exitCode, - stdout: new TextDecoder().decode(output.stdout), - }; - } -} -``` - - -#### Parameters - -- `cmd` (`string[]`) — executable followed by its arguments. -- `options` (`ContainerExecOptions`, optional) — process configuration: - - `stdin` (`ReadableStream | "pipe"`) — source for standard input. Use `"pipe"` to write through the returned `stdin` stream. When omitted, standard input closes and sends end-of-file (EOF). - - `stdout` (`"pipe" | "ignore"`, default `"pipe"`) — captures or discards standard output. - - `stderr` (`"pipe" | "ignore" | "combined"`, default `"pipe"`) — captures, discards, or merges standard error into standard output. The `"combined"` value requires `stdout: "pipe"`. Combined output does not guarantee ordering between its source streams. - - `cwd` (`string`) — working directory for the process. - - `env` (`Record`) — environment additions and overrides. The process inherits existing Container variables. Matching keys use the per-execution value. - - `user` (`string`) — image user for the process. - -#### Return values - -Returns `Promise`. - -An `ExecProcess` has these fields and methods: - -- `stdin` (`WritableStream | null`) — writable standard input when `stdin` is `"pipe"`. -- `stdout` (`ReadableStream | null`) — readable standard output when piped. -- `stderr` (`ReadableStream | null`) — readable standard error when piped separately. -- `pid` (`number`) — process identifier. -- `exitCode` (`Promise`) — resolves when the process exits. Nonzero codes resolve normally instead of rejecting. -- `output()` (`Promise`) — reads buffered output once. `ExecOutput` contains `stdout` (`ArrayBuffer`), `stderr` (`ArrayBuffer`), and `exitCode` (`number`). Ignored streams produce empty buffers. Use `TextDecoder` to decode text. -- `kill(signal?: number)` (`void`) — queues a signal for the process. The default is `SIGTERM`, signal `15`. The signal must be from `1` through `64`. - -With `stderr: "combined"`, `stderr` is `null` on `ExecProcess` and an empty `ArrayBuffer` on `ExecOutput`. Read both output channels from `stdout`. - -`output()` throws a `TypeError` when called more than once or after either readable stream starts being consumed. For large output, consume both readable streams concurrently instead of buffering them with `output()`. - -`exec` has no built-in timeout. Use `kill()` to request termination, then observe completion through `exitCode`. A process can handle or ignore a signal, so this does not enforce a hard deadline. Do not infer a specific exit code from the signal. - -#### Exceptions - -- `exec()` throws when the Container is not running. -- `exec()` throws a `TypeError` when `cmd` is empty, an option mode is invalid, or `stderr: "combined"` is used with `stdout: "ignore"`. -- `exec()` rejects if the runtime cannot create or start the process. -- Environment variable names cannot contain `=` or null characters. Environment values, `cwd`, and `user` cannot contain null characters. -- `kill()` throws a `RangeError` when the signal is outside the supported range. - -For task-oriented examples, refer to [Execute commands](/containers/guides/execute-commands/). - -### `destroy` - -`destroy` stops the container and optionally returns a custom error message to the `monitor()` error callback. - -```js -this.ctx.container.destroy("Manually Destroyed"); -``` - -#### Parameters - -- `error` (optional): A string that will be sent to the error handler of the `monitor` method. This is useful for logging or debugging purposes. - -#### Return values - -- A promise that returns once the container is destroyed. - -### `signal` - -`signal` sends an IPC signal to the container, such as SIGKILL or SIGTERM. This is useful for stopping the container gracefully or forcefully. - -```js -const SIGTERM = 15; -this.ctx.container.signal(SIGTERM); -``` - -#### Parameters - -- `signal`: a number representing the signal to send to the container. This is typically a POSIX signal number, such as SIGTERM (15) or SIGKILL (9). - -#### Return values - -- None. - -### `getTcpPort` - -`getTcpPort` returns a TCP port from the container. This can be used to communicate with the container over TCP and HTTP. - -```js -const port = this.ctx.container.getTcpPort(8080); -const res = await port.fetch("http://container/set-state", { - body: initialState, - method: "POST", -}); -``` - -```js -const conn = this.ctx.container.getTcpPort(8080).connect("10.0.0.1:8080"); -await conn.opened; - -try { - if (request.body) { - await request.body.pipeTo(conn.writable); - } - return new Response(conn.readable); -} catch (err) { - console.error("Request body piping failed:", err); - return new Response("Failed to proxy request body", { status: 502 }); -} -``` - -#### Parameters - -- `port` (number): a TCP port number to use for communication with the container. - -#### Return values - -- `TcpPort`: a `TcpPort` object representing the TCP port. This object can be used to send requests to the container over TCP and HTTP. - -### `monitor` - -`monitor` returns a promise that resolves when a container exits and errors if a container errors. This is useful for setting up -callbacks to handle container status changes in your Workers code. - -```js -class MyContainer extends DurableObject { - constructor(ctx, env) { - super(ctx, env); - function onContainerExit() { - console.log("Container exited"); - } - - // the "err" value can be customized by the destroy() method - async function onContainerError(err) { - console.log("Container errored", err); - } - - this.ctx.container.start(); - this.ctx.container.monitor().then(onContainerExit).catch(onContainerError); - } -} -``` - -#### Parameters - -- None - -#### Return values - -- A promise that resolves when the container exits. - -### `interceptOutboundHttp` - -`interceptOutboundHttp` routes outbound HTTP requests matching a hostname, hostname glob, IP address, IP:port, or CIDR range through a `WorkerEntrypoint`. Can be called before or after starting the container. Open connections pick up the new handler without being dropped. - -```js -const worker = this.ctx.exports.MyWorker({ props: { message: "hello" } }); - -// Match a specific hostname -this.ctx.container.interceptOutboundHttp("api.example.com", worker); - -// Match a hostname glob pattern -this.ctx.container.interceptOutboundHttp("*.example.com", worker); - -// Match an IP:port -await this.ctx.container.interceptOutboundHttp("15.0.0.1:80", worker); - -// Match a CIDR range (IPv4 and IPv6) -await this.ctx.container.interceptOutboundHttp("123.123.123.123/23", worker); -``` - -#### Parameters - -- `target` (string): A hostname, hostname glob (for example, `*.example.com`), IP address, IP:port, or CIDR range to match. -- `worker` (WorkerEntrypoint): A `WorkerEntrypoint` instance to handle matching requests. - -#### Return values - -- None. - -### `interceptAllOutboundHttp` - -`interceptAllOutboundHttp` routes all outbound HTTP requests from the container through a `WorkerEntrypoint`, regardless of destination. - -```js -await this.ctx.container.interceptAllOutboundHttp(worker); -``` - -#### Parameters - -- `worker` (WorkerEntrypoint): A `WorkerEntrypoint` instance to handle all outbound HTTP requests. - -#### Return values - -- A promise that resolves once the intercept rule is installed. - -### `interceptOutboundHttps` - -`interceptOutboundHttps` routes outbound HTTPS requests matching a hostname or hostname glob through a `WorkerEntrypoint`. Works the same way as `interceptOutboundHttp` but for HTTPS traffic. The container must trust the CA certificate at `/etc/cloudflare/certs/cloudflare-containers-ca.crt` for HTTPS interception to work. - -Supports glob patterns where `*` matches any sequence of characters. - -```js -const worker = this.ctx.exports.MyWorker({ props: {} }); - -// Match a specific hostname -this.ctx.container.interceptOutboundHttps("api.example.com", worker); - -// Match a hostname glob pattern -this.ctx.container.interceptOutboundHttps("*.example.com", worker); - -// Intercept all HTTPS traffic -this.ctx.container.interceptOutboundHttps("*", worker); -``` - -#### Parameters - -- `target` (string): A hostname or hostname glob pattern to match. Use `*` to intercept all HTTPS traffic. -- `worker` (WorkerEntrypoint): A `WorkerEntrypoint` instance to handle matching requests. - -#### Return values - -- None. - -## Related resources - -- [Container class reference](/containers/reference/container-class/) — the recommended high-level API built on top of this interface -- [Containers overview](/containers/) -- [Get started with Containers](/containers/get-started/) -- [SQLite storage API](/durable-objects/api/sqlite-storage-api/) — persist state across container restarts -- [Durable Objects](/durable-objects/) — the underlying platform that powers Containers diff --git a/src/content/docs/reference-architecture/diagrams/ai/enterprise-ai-vibe-coding-platform.mdx b/src/content/docs/reference-architecture/diagrams/ai/enterprise-ai-vibe-coding-platform.mdx index 52c4c03ea23..7bb16c681c4 100644 --- a/src/content/docs/reference-architecture/diagrams/ai/enterprise-ai-vibe-coding-platform.mdx +++ b/src/content/docs/reference-architecture/diagrams/ai/enterprise-ai-vibe-coding-platform.mdx @@ -54,7 +54,7 @@ With a local agent harness, developers use CLI-based tools like Cursor, Windsurf All LLM interactions are tracked and managed through [AI Gateway](/ai-gateway/), which provides provider routing, cost controls, prompt logging, and [DLP inspection](/cloudflare-one/data-loss-prevention/). [Cost tracking](/ai-gateway/observability/costs/) attributes usage to projects, teams, departments, and individual users. -All egress from the development environment is controlled at the platform level. For containers, an [outbound handler](/containers/guides/outbound-traffic/) intercepts HTTP traffic. For Dynamic Workers, [egress control](/dynamic-workers/usage/egress-control/) provides equivalent capabilities. Secrets required for downstream connectivity are stored in [Secrets Store](/secrets-store/) and injected by the outbound handler at the platform level. The sandboxed environment never has direct access to credentials. With this outbound handler, platform administrators can allow or deny specific origin destinations, reroute traffic, apply custom policies on outbound traffic, or connect to other Cloudflare resources through [bindings](/workers/runtime-apis/bindings/). For access to on-premises or internal systems, [Workers VPC](/workers-vpc/) establishes private connectivity without exposing those systems to the Internet. +All egress from the development environment is controlled at the platform level. For containers, an [outbound handler](/containers/configuration/outbound-traffic/) intercepts HTTP traffic. For Dynamic Workers, [egress control](/dynamic-workers/usage/egress-control/) provides equivalent capabilities. Secrets required for downstream connectivity are stored in [Secrets Store](/secrets-store/) and injected by the outbound handler at the platform level. The sandboxed environment never has direct access to credentials. With this outbound handler, platform administrators can allow or deny specific origin destinations, reroute traffic, apply custom policies on outbound traffic, or connect to other Cloudflare resources through [bindings](/workers/runtime-apis/bindings/). For access to on-premises or internal systems, [Workers VPC](/workers-vpc/) establishes private connectivity without exposing those systems to the Internet. Additional security controls can be layered into the development container through package version locking and organizational controls baked into the container image. If the harness uses MCP servers, [MCP portals](/cloudflare-one/access-controls/ai-controls/mcp-portals/) provide audit logging of tool invocations, permission management for tool access, and visibility into which tools agents use and what data they access. @@ -80,4 +80,4 @@ The dispatch worker can set [custom limits](/cloudflare-for-platforms/workers-fo Observability operates at two levels. At the platform level, [Workers Trace Events Logpush](/cloudflare-for-platforms/workers-for-platforms/configuration/observability/) enabled on the dispatch worker covers all user workers in the namespace. The [GraphQL Analytics API](/analytics/graphql-api/) queries by `dispatchNamespaceName` for aggregate metrics across the platform. At the app level, [Tail Workers](/cloudflare-for-platforms/workers-for-platforms/configuration/observability/) can be attached to individual user workers for granular logging. [Workers Analytics Engine](/analytics/analytics-engine/) lets the platform write and query events by script tag, surfacing per-app usage metrics to individual users. -As the number of vibe-coded applications grows, the platform metadata store serves as an application registry, surfacing the owner, team, description, connected data sources, and usage metrics for each deployment. As with the development plane, this metadata can be stored in any preferred data store such as [D1](/d1/), [KV](/kv/), or [R2](/r2/). This allows employees to find existing tools before duplicating efforts. [Resource tagging](/resource-tagging/) applied to the underlying Cloudflare resources links each user worker back to its owner and cost attribution. \ No newline at end of file +As the number of vibe-coded applications grows, the platform metadata store serves as an application registry, surfacing the owner, team, description, connected data sources, and usage metrics for each deployment. As with the development plane, this metadata can be stored in any preferred data store such as [D1](/d1/), [KV](/kv/), or [R2](/r2/). This allows employees to find existing tools before duplicating efforts. [Resource tagging](/resource-tagging/) applied to the underlying Cloudflare resources links each user worker back to its owner and cost attribution. diff --git a/src/content/docs/sandbox/guides/outbound-traffic.mdx b/src/content/docs/sandbox/guides/outbound-traffic.mdx index e3da306c4a5..70b871df68a 100644 --- a/src/content/docs/sandbox/guides/outbound-traffic.mdx +++ b/src/content/docs/sandbox/guides/outbound-traffic.mdx @@ -289,6 +289,6 @@ Requests are evaluated in this order: ## Related resources - [Connect to Workers bindings](/sandbox/guides/workers-connections/) — Access KV, R2, Durable Objects, and other bindings from a sandbox -- [Handle outbound traffic (Containers)](/containers/guides/outbound-traffic/) — Container SDK API for outbound handlers +- [Handle outbound traffic (Containers)](/containers/configuration/outbound-traffic/) — Container SDK API for outbound handlers - [Sandbox options](/sandbox/configuration/sandbox-options/) — Configure sandbox behavior - [Environment variables](/sandbox/configuration/environment-variables/) — Configure secrets and environment variables diff --git a/src/content/docs/workers/wrangler/configuration.mdx b/src/content/docs/workers/wrangler/configuration.mdx index e7535aeb3bb..e08776c28c2 100644 --- a/src/content/docs/workers/wrangler/configuration.mdx +++ b/src/content/docs/workers/wrangler/configuration.mdx @@ -1271,7 +1271,7 @@ The following options are available: build and push the image, or it can be an image reference. Supported registries are the Cloudflare Registry, Docker Hub, Amazon ECR, and Google Artifact Registry. For more information, refer to [Image Management](/containers/guides/image-management/). - `class_name` - The corresponding Durable Object class name. This will make this Durable Object a container-enabled Durable Object - and allow each instance to control a container. See [Durable Object Container Methods](/durable-objects/api/container/) for details. + and allow each instance to control a container. Refer to the [Durable Object Container API](/containers/api/durable-object-container/) for details. - `instance_type` - The instance type of the container. This determines the amount of memory, CPU, and disk given to the container instance. The current options are `"lite"`, `"basic"`, `"standard-1"`, `"standard-2"`, `"standard-3"`, and `"standard-4"`. The default is `"lite"`. For more information,