From a8cb7fe4d3bc34519870b26fa17bc05b3e8e88b8 Mon Sep 17 00:00:00 2001 From: emily-shen Date: Fri, 22 Aug 2025 16:59:33 +0100 Subject: [PATCH 1/8] restructure --- src/content/docs/containers/beta-info.mdx | 2 +- .../docs/containers/container-package.mdx | 2 +- src/content/docs/containers/faq.mdx | 4 +- src/content/docs/containers/get-started.mdx | 2 +- src/content/docs/containers/local-dev.mdx | 2 +- .../{ => platform-details}/architecture.mdx | 6 +-- .../durable-object-methods.mdx | 0 .../environment-variables.mdx | 38 +++++++++++++++++++ .../image-management.mdx | 0 .../containers/platform-details/index.mdx | 8 ++++ .../limits.mdx} | 36 +----------------- .../scaling-and-routing.mdx | 0 src/content/docs/containers/pricing.mdx | 2 +- .../docs/containers/wrangler-commands.mdx | 2 +- .../containers/wrangler-configuration.mdx | 2 +- 15 files changed, 58 insertions(+), 48 deletions(-) rename src/content/docs/containers/{ => platform-details}/architecture.mdx (97%) rename src/content/docs/containers/{ => platform-details}/durable-object-methods.mdx (100%) create mode 100644 src/content/docs/containers/platform-details/environment-variables.mdx rename src/content/docs/containers/{ => platform-details}/image-management.mdx (100%) create mode 100644 src/content/docs/containers/platform-details/index.mdx rename src/content/docs/containers/{platform-details.mdx => platform-details/limits.mdx} (63%) rename src/content/docs/containers/{ => platform-details}/scaling-and-routing.mdx (100%) diff --git a/src/content/docs/containers/beta-info.mdx b/src/content/docs/containers/beta-info.mdx index 4afe156719af3cc..591dc3a5f37a5a6 100644 --- a/src/content/docs/containers/beta-info.mdx +++ b/src/content/docs/containers/beta-info.mdx @@ -2,7 +2,7 @@ pcx_content_type: reference title: Beta Info & Roadmap sidebar: - order: 2 + order: 9 --- Currently, Containers are in beta. There are several changes we plan to make prior to GA: diff --git a/src/content/docs/containers/container-package.mdx b/src/content/docs/containers/container-package.mdx index 81c5b60b713b3b5..a89bfe78e0e7f32 100644 --- a/src/content/docs/containers/container-package.mdx +++ b/src/content/docs/containers/container-package.mdx @@ -2,7 +2,7 @@ pcx_content_type: navigation title: Container Package sidebar: - order: 8 + order: 5 --- When writing code that interacts with a container instance, you can either use a diff --git a/src/content/docs/containers/faq.mdx b/src/content/docs/containers/faq.mdx index e164b6e1e4da25d..5c4fe4e8f56b172 100644 --- a/src/content/docs/containers/faq.mdx +++ b/src/content/docs/containers/faq.mdx @@ -2,9 +2,7 @@ pcx_content_type: navigation title: Frequently Asked Questions sidebar: - order: 5 - group: - hideIndex: true + order: 10 --- import { WranglerConfig } from "~/components"; diff --git a/src/content/docs/containers/get-started.mdx b/src/content/docs/containers/get-started.mdx index 5f6b9926a39e1b6..e31c7b0e375a986 100644 --- a/src/content/docs/containers/get-started.mdx +++ b/src/content/docs/containers/get-started.mdx @@ -2,7 +2,7 @@ pcx_content_type: get-started title: Getting started sidebar: - order: 1 + order: 2 --- import { WranglerConfig, PackageManagers } from "~/components"; diff --git a/src/content/docs/containers/local-dev.mdx b/src/content/docs/containers/local-dev.mdx index 912dd9771890a30..9a028c02ab29199 100644 --- a/src/content/docs/containers/local-dev.mdx +++ b/src/content/docs/containers/local-dev.mdx @@ -2,7 +2,7 @@ pcx_content_type: reference title: Local Development sidebar: - order: 3 + order: 6 --- You can run both your container and your Worker locally, without additional configuration, by running [`npx wrangler dev`](/workers/wrangler/commands/#dev) (or `vite dev` for Vite projects using the [Cloudflare Vite plugin](/workers/vite-plugin/)) in your project's directory. diff --git a/src/content/docs/containers/architecture.mdx b/src/content/docs/containers/platform-details/architecture.mdx similarity index 97% rename from src/content/docs/containers/architecture.mdx rename to src/content/docs/containers/platform-details/architecture.mdx index e4e8269b77fe1cc..771222f86c70d2d 100644 --- a/src/content/docs/containers/architecture.mdx +++ b/src/content/docs/containers/platform-details/architecture.mdx @@ -1,12 +1,10 @@ --- pcx_content_type: reference -title: Architecture +title: Container Lifecycle sidebar: - order: 9 + order: 1 --- -This page describes the architecture of Cloudflare Containers. - ## How and where containers run After you deploy a Worker that uses a Container, your image is uploaded to diff --git a/src/content/docs/containers/durable-object-methods.mdx b/src/content/docs/containers/platform-details/durable-object-methods.mdx similarity index 100% rename from src/content/docs/containers/durable-object-methods.mdx rename to src/content/docs/containers/platform-details/durable-object-methods.mdx diff --git a/src/content/docs/containers/platform-details/environment-variables.mdx b/src/content/docs/containers/platform-details/environment-variables.mdx new file mode 100644 index 000000000000000..4b62e051d25430b --- /dev/null +++ b/src/content/docs/containers/platform-details/environment-variables.mdx @@ -0,0 +1,38 @@ +--- +pcx_content_type: reference +title: Environment Variables +sidebar: + order: 7 +--- + +import { WranglerConfig } from "~/components"; + +## Runtime environment variables + +The container runtime automatically sets the following variables: + +- `CLOUDFLARE_APPLICATION_ID` - the ID of the Containers application +- `CLOUDFLARE_COUNTRY_A2` - the [ISO 3166-1 Alpha 2 code](https://www.iso.org/obp/ui/#search/code/) of a country the container is placed in +- `CLOUDFLARE_DEPLOYMENT_ID` - the ID of the container instance +- `CLOUDFLARE_LOCATION` - a name of a location the container is placed in +- `CLOUDFLARE_PLACEMENT_ID` - a placement ID +- `CLOUDFLARE_REGION` - a region name +- `CLOUDFLARE_DURABLE_OBJECT_ID` - the ID of the Durable Object that the container is bound to + +## Custom environment variables + +Custom environment variables can be set when defining a Container in your Worker: + +```javascript +class MyContainer extends Container { + defaultPort = 4000; + envVars = { + MY_CUSTOM_VAR: "value", + ANOTHER_VAR: "another_value", + }; +} +``` + +:::note +If you supply environment variables with the same names, supplied values will override predefined values. +::: diff --git a/src/content/docs/containers/image-management.mdx b/src/content/docs/containers/platform-details/image-management.mdx similarity index 100% rename from src/content/docs/containers/image-management.mdx rename to src/content/docs/containers/platform-details/image-management.mdx diff --git a/src/content/docs/containers/platform-details/index.mdx b/src/content/docs/containers/platform-details/index.mdx new file mode 100644 index 000000000000000..4e6dd8f5cf20af2 --- /dev/null +++ b/src/content/docs/containers/platform-details/index.mdx @@ -0,0 +1,8 @@ +--- +pcx_content_type: navigation +title: Platform Reference +sidebar: + order: 4 + group: + hideIndex: true +--- diff --git a/src/content/docs/containers/platform-details.mdx b/src/content/docs/containers/platform-details/limits.mdx similarity index 63% rename from src/content/docs/containers/platform-details.mdx rename to src/content/docs/containers/platform-details/limits.mdx index 7c50684e2fa7a75..53e62996b49f266 100644 --- a/src/content/docs/containers/platform-details.mdx +++ b/src/content/docs/containers/platform-details/limits.mdx @@ -1,14 +1,10 @@ --- -pcx_content_type: navigation -title: Platform +pcx_content_type: reference +title: Limits and Instance Types sidebar: order: 2 - group: - hideIndex: true --- -import { WranglerConfig } from "~/components"; - ## Instance Types The memory, vCPU, and disk space for Containers are set through predefined instance types. @@ -37,31 +33,3 @@ While in open beta, the following limits are currently in effect: [^1]: This limit will be raised as we continue the beta. [^2]: Delete container images with `wrangler containers delete` to free up space. Note that if you delete a container image and then [roll back](/workers/configuration/versions-and-deployments/rollbacks/) your Worker to a previous version, this version may no longer work. - -## Environment variables - -The container runtime automatically sets the following variables: - -- `CLOUDFLARE_APPLICATION_ID` - the ID of the Containers application -- `CLOUDFLARE_COUNTRY_A2` - the [ISO 3166-1 Alpha 2 code](https://www.iso.org/obp/ui/#search/code/) of a country the container is placed in -- `CLOUDFLARE_DEPLOYMENT_ID` - the ID of the container instance -- `CLOUDFLARE_LOCATION` - a name of a location the container is placed in -- `CLOUDFLARE_PLACEMENT_ID` - a placement ID -- `CLOUDFLARE_REGION` - a region name -- `CLOUDFLARE_DURABLE_OBJECT_ID` - the ID of the Durable Object that the container is bound to - -:::note -If you supply environment variables with the same names, supplied values will override predefined values. -::: - -Custom environment variables can be set when defining a Container in your Worker: - -```javascript -class MyContainer extends Container { - defaultPort = 4000; - envVars = { - MY_CUSTOM_VAR: "value", - ANOTHER_VAR: "another_value", - }; -} -``` diff --git a/src/content/docs/containers/scaling-and-routing.mdx b/src/content/docs/containers/platform-details/scaling-and-routing.mdx similarity index 100% rename from src/content/docs/containers/scaling-and-routing.mdx rename to src/content/docs/containers/platform-details/scaling-and-routing.mdx diff --git a/src/content/docs/containers/pricing.mdx b/src/content/docs/containers/pricing.mdx index 88bd4002bd0502e..8764044beca0894 100644 --- a/src/content/docs/containers/pricing.mdx +++ b/src/content/docs/containers/pricing.mdx @@ -2,7 +2,7 @@ pcx_content_type: reference title: Pricing sidebar: - order: 4 + order: 11 --- ## vCPU, Memory and Disk diff --git a/src/content/docs/containers/wrangler-commands.mdx b/src/content/docs/containers/wrangler-commands.mdx index b36d8b8d88e4176..ffe30f7104b6c20 100644 --- a/src/content/docs/containers/wrangler-commands.mdx +++ b/src/content/docs/containers/wrangler-commands.mdx @@ -3,5 +3,5 @@ pcx_content_type: navigation title: Wrangler Commands external_link: /workers/wrangler/commands/#containers sidebar: - order: 70 + order: 8 --- diff --git a/src/content/docs/containers/wrangler-configuration.mdx b/src/content/docs/containers/wrangler-configuration.mdx index a1ca245635efeff..f80d5e3115cb374 100644 --- a/src/content/docs/containers/wrangler-configuration.mdx +++ b/src/content/docs/containers/wrangler-configuration.mdx @@ -3,5 +3,5 @@ pcx_content_type: navigation title: Wrangler Configuration external_link: /workers/wrangler/configuration/#containers sidebar: - order: 60 + order: 7 --- From 08ee8596e852128a3972aaf0eec632803a46ba29 Mon Sep 17 00:00:00 2001 From: emily-shen Date: Fri, 22 Aug 2025 17:00:23 +0100 Subject: [PATCH 2/8] new rollouts page --- src/content/docs/containers/faq.mdx | 13 +----- .../containers/platform-details/rollouts.mdx | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+), 12 deletions(-) create mode 100644 src/content/docs/containers/platform-details/rollouts.mdx diff --git a/src/content/docs/containers/faq.mdx b/src/content/docs/containers/faq.mdx index 5c4fe4e8f56b172..ab8023b69472fbf 100644 --- a/src/content/docs/containers/faq.mdx +++ b/src/content/docs/containers/faq.mdx @@ -64,18 +64,7 @@ An Example: ## How do container updates and rollouts work? -When you run `wrangler deploy`, the Worker code is updated immediately and Container -instances are updated using a rolling deploy strategy. The default rollout configuration is two steps, -where the first step updates 10% of the instances, and the second step updates the remaining 90%. -This can be configured in your Wrangler config file using the [`rollout_step_percentage`](/workers/wrangler/configuration#containers) property. - -When deploying a change, you can also configure a [`rollout_active_grace_period`](/workers/wrangler/configuration#containers), which is the minimum -number of seconds to wait before an active container instance becomes eligible for updating during a rollout. -At that point, the container will be sent at `SIGTERM`, and still has 15 minutes to shut down gracefully. -If the instance does not stop within 15 minutes, it is forcefully stopped with a `SIGKILL` signal. -If you have cleanup that must occur before a Container instance is stopped, you should do it during this 15 minute period. - -Once stopped, the instance is replaced with a new instance running the updated code. Requests may hang while the container is starting up again. +See [rollout documentation](/containers/platform-details/rollouts/) for details. ## How does scaling work? diff --git a/src/content/docs/containers/platform-details/rollouts.mdx b/src/content/docs/containers/platform-details/rollouts.mdx new file mode 100644 index 000000000000000..4bcf90751979836 --- /dev/null +++ b/src/content/docs/containers/platform-details/rollouts.mdx @@ -0,0 +1,43 @@ +--- +pcx_content_type: reference +title: Rollouts +sidebar: + order: 2 +--- + +import { WranglerConfig } from "~/components"; + +## How rollouts work +When you run `wrangler deploy`, the Worker code is updated immediately and Container +instances are updated using a rolling deploy strategy. The default rollout configuration is two steps, +where the first step updates 10% of the instances, and the second step updates the remaining 90%. +This can be configured in your Wrangler config file using the [`rollout_step_percentage`](/workers/wrangler/configuration#containers) property. + +When deploying a change, you can also configure a [`rollout_active_grace_period`](/workers/wrangler/configuration#containers), which is the minimum +number of seconds to wait before an active container instance becomes eligible for updating during a rollout. +At that point, the container will be sent at `SIGTERM`, and still has 15 minutes to shut down gracefully. +If the instance does not stop within 15 minutes, it is forcefully stopped with a `SIGKILL` signal. +If you have cleanup that must occur before a Container instance is stopped, you should do it during this 15 minute period. + +Once stopped, the instance is replaced with a new instance running the updated code. Requests may hang while the container is starting up again. + +Here is an example configuration that sets a 5 minute grace period and a two step rollout where the first step updates 10% of instances and the second step updates 100% of instances: + + +```toml +[[containers]] +max_instances = 10 +class_name = "MyContainer" +image = "./Dockerfile" +rollout_active_grace_period = 300 +rollout_step_percentage = [10, 100] + +[[durable_objects.bindings]] +name = "MY_CONTAINER" +class_name = "MyContainer" + +[[migrations]] +tag = "v1" +new_sqlite_classes = ["MyContainer"] +``` + \ No newline at end of file From 22f6208edf0eaa67b81fb7aa1c8d26856ae430b5 Mon Sep 17 00:00:00 2001 From: emily-shen Date: Wed, 3 Sep 2025 13:52:16 +0100 Subject: [PATCH 3/8] rewrite architecture page as lifecycle --- .../platform-details/architecture.mdx | 127 +++++++++++++----- 1 file changed, 92 insertions(+), 35 deletions(-) diff --git a/src/content/docs/containers/platform-details/architecture.mdx b/src/content/docs/containers/platform-details/architecture.mdx index 771222f86c70d2d..e7c9b48987027bd 100644 --- a/src/content/docs/containers/platform-details/architecture.mdx +++ b/src/content/docs/containers/platform-details/architecture.mdx @@ -1,56 +1,46 @@ --- pcx_content_type: reference -title: Container Lifecycle +title: Lifecycle of a Container sidebar: order: 1 --- -## How and where containers run +## Deployment -After you deploy a Worker that uses a Container, your image is uploaded to -[Cloudflare's Registry](/containers/image-management) and distributed globally to Cloudflare's Network. +After you deploy an application with a Container, your image is uploaded to +[Cloudflare's Registry](/containers/platform-details/image-management) and distributed globally to Cloudflare's Network. Cloudflare will pre-schedule instances and pre-fetch images across the globe to ensure quick start -times when scaling up the number of concurrent container instances. This allows you to call -`env.YOUR_CONTAINER.get(id)` and get a new instance quickly without worrying -about the underlying scaling. +times when scaling up the number of concurrent container instances. -When a request is made to start a new container instance, the nearest location -with a pre-fetched image is selected. Subsequent requests to the same instance, -regardless of where they originate, will be routed to this location as long as -the instance stays alive. +Unlike Workers, which are updated immediately on deploy, container instances are updated using a rolling deploy strategy. +This allows you to gracefully shutdown any running instances during a rollout. Refer to [rollouts](/containers/platform-details/rollouts/) for more details. -Starting additional container instances will use other locations with pre-fetched images, -and Cloudflare will automatically begin prepping additional machines behind the scenes -for additional scaling and quick cold starts. Because there are a finite number pre-warmed -locations, some container instances may be started in locations that are farther away from -the end-user. This is done to ensure that the container instance starts quickly. You are -only charged for actively running instances and not for any unused pre-warmed images. +## Lifecycle of a Request -Each container instance runs inside its own VM, which provides strong -isolation from other workloads running on Cloudflare's network. Containers -should be built for the `linux/amd64` architecture, and should stay within -[size limits](/containers/platform-details/#limits). Logging, metrics collection, and -networking are automatically set up on each container. +### Client to Worker -## Life of a Container Request - -When a request is made to any Worker, including one with an associated Container, it is generally handled +Recall that Containers are backed by Durable Objects and Workers. +Requests are first routed through a Worker, which is generally handled by a datacenter in a location with the best latency between itself and the requesting user. A different datacenter may be selected to optimize overall latency, if [Smart Placement](/workers/configuration/smart-placement/) is on, or if the nearest location is under heavy load. -When a request is made to a Container instance, it is sent through a Durable Object, which -can be defined by either using a `DurableObject` or the [`Container` class](/containers/container-package), which -extends Durable Objects with Container-specific APIs and helpers. We recommend using `Container`, see -the [`Container` class documentation](/containers/container-package) for more details. +Because all Container requests are passed through a Worker, end-users cannot make TCP or +UDP requests to a Container instance. If you have a use case that requires inbound TCP +or UDP from an end-user, please [let us know](https://forms.gle/AGSq54VvUje6kmKu8). + +### Worker to Durable Object -Each Durable Object is a globally routable isolate that can execute code and store state. This allows +From the Worker, a request passes through a Durable Object instance (the [Container package](/containers/container-package) extends a Durable Object 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. -As mentioned above, when a container instance starts, it is launched in the nearest pre-warmed location. This means that -code in a container is usually executed in a different location than the one handling the Workers request. +### Starting a Container + +When a Durable Object instance requests to start a new container instance, the **nearest location +with a pre-fetched image** is selected. :::note Currently, Durable Objects may be co-located with their associated Container instance, but often are not. @@ -59,6 +49,73 @@ Cloudflare is currently working on expanding the number of locations in which a which will allow container instances to always run in the same location as their Durable Object. ::: -Because all Container requests are passed through a Worker, end-users cannot make TCP or -UDP requests to a Container instance. If you have a use case that requires inbound TCP -or UDP from an end-user, please [let us know](https://forms.gle/AGSq54VvUje6kmKu8). +Starting additional container instances will use other locations with pre-fetched images, +and Cloudflare will automatically begin prepping additional machines behind the scenes +for additional scaling and quick cold starts. Because there are a finite number of pre-warmed +locations, some container instances may be started in locations that are farther away from +the end-user. This is done to ensure that the container instance starts quickly. You are +only charged for actively running instances and not for any unused pre-warmed images. + +#### Cold starts + +A cold start is when a container instance is started from a completely stopped state. +If you call `env.MY_CONTAINER.get(id)` with a completely novel ID and launch +this instance for the first time, it will result in a cold start. +This will start the container image from its entrypoint for the first time. Depending +on what this entrypoint does, it will take a variable amount of time to start. + +Container cold starts can often be the 2-3 second range, but this is dependent +on image size and code execution time, among other factors. + +### Requests to running Containers + +When a request _starts_ a new container instance, the nearest location with a pre-fetched image is selected. +Subsequent requests to a particular instance, regardless of where they originate, will be routed to this location as long as +the instance stays alive. + +However, once that container instance stops and restarts, future requests could be routed to a _different_ location. +This location will again be the nearest location to the originating request with a pre-fetched image. + +### Container runtime + +Each container instance runs inside its own VM, which provides strong +isolation from other workloads running on Cloudflare's network. Containers +should be built for the `linux/amd64` architecture, and should stay within +[size limits](/containers/platform-details/limits). [Logging](containers/faq/#how-do-container-logs-work), metrics collection, and +networking are automatically set up on each container, if configured by the developer. + +### After a request + +Cloudflare will not actively shut off a container instance after a specific amount of +time. If you do not set `sleepAfter` on your Container class, or stop the instance +manually, it will continue to run unless its host server is restarted. This +happens on an irregular cadence, but frequently enough where Cloudflare does not +guarantee that any instance will run for any set period of time. + +When a container instance is going to be shut down, it is sent a `SIGTERM` signal, +and then a `SIGKILL` signal after 15 minutes. You should perform any necessary +cleanup to ensure a graceful shutdown in this time. The container instance +will be rebooted elsewhere shortly after this. + +All disk is ephemeral. When a Container instance goes to sleep, the next time +it is started, it will have a fresh disk as defined by its container image. + +Persistent disk is something the Cloudflare team is exploring in the future, but +is not slated for the near term. + +## An example request + +- A developer deploys a Container. Cloudflare automatically readies instances across its Network. +- A request is made from a client in Bariloche, Argentina. It reaches the Worker in a nearby + Cloudflare location in Neuquen, Argentina. +- This Worker request calls `getContainer(env.MY_CONTAINER, "session-1337")`. Under the hood, this brings up a Durable + Object, which then calls `this.ctx.container.start`. +- This requests the nearest free Container instance. Cloudflare recognizes that an instance is free in Buenos Aires, Argentina, and + starts it there. +- A different user needs to route to the same container. This user's request reaches + the Worker running in Cloudflare's location in San Diego, US. +- The Worker again calls `getContainer(env.MY_CONTAINER, "session-1337")`. +- If the initial container instance is still running, the request is routed to the original location + in Buenos Aires. If the initial container has gone to sleep, Cloudflare will once + again try to find the nearest "free" instance of the Container, likely + one in North America, and start an instance there. From 152b9a3e71d190d944ef032e7b2c7b2e5a81140f Mon Sep 17 00:00:00 2001 From: emily-shen Date: Wed, 3 Sep 2025 13:52:29 +0100 Subject: [PATCH 4/8] miscellaneous fixes --- public/__redirects | 7 +- src/content/docs/containers/beta-info.mdx | 5 +- .../docs/containers/container-package.mdx | 2 +- .../containers/examples/container-backend.mdx | 18 +-- src/content/docs/containers/examples/cron.mdx | 15 ++- .../examples/env-vars-and-secrets.mdx | 33 +++-- .../docs/containers/examples/stateless.mdx | 3 +- src/content/docs/containers/faq.mdx | 4 +- src/content/docs/containers/get-started.mdx | 14 ++- src/content/docs/containers/index.mdx | 116 ++++++++++-------- src/content/docs/containers/local-dev.mdx | 15 ++- .../platform-details/architecture.mdx | 2 +- .../environment-variables.mdx | 10 +- .../platform-details/image-management.mdx | 43 +++---- .../platform-details/scaling-and-routing.mdx | 25 ++-- 15 files changed, 175 insertions(+), 137 deletions(-) diff --git a/public/__redirects b/public/__redirects index 8f2e0944cd84dbb..1d24e3ba36fd521 100644 --- a/public/__redirects +++ b/public/__redirects @@ -2421,4 +2421,9 @@ /ai-gateway/guardrails/* /ai-gateway/features/guardrails/:splat 301 /ai-gateway/websockets-api/* /ai-gateway/usage/websockets-api/:splat 301 - +# Containers +/containers/image-management /containers/platform-details/image-management 301 +/containers/scaling-and-routing /containers/platform-details/scaling-and-routing 301 +/containers/architecture /containers/platform-details/architecture 301 +/containers/durable-object-methods /containers/platform-details/durable-object-methods 301 +/containers/platform-details /containers/platform-details/architecture 301 \ No newline at end of file diff --git a/src/content/docs/containers/beta-info.mdx b/src/content/docs/containers/beta-info.mdx index 591dc3a5f37a5a6..b0877ec8f7ed8c3 100644 --- a/src/content/docs/containers/beta-info.mdx +++ b/src/content/docs/containers/beta-info.mdx @@ -24,7 +24,7 @@ by calling `get()` on their binding with a unique ID. We plan to add official support for utilization-based autoscaling and latency-aware load balancing in the future. -See the [Autoscaling documentation](/containers/scaling-and-routing) for more information. +See the [Autoscaling documentation](/containers/platform-details/scaling-and-routing) for more information. ### Reduction of log noise @@ -38,7 +38,6 @@ We plan to automatically reduce log noise in the future. The dashboard will be updated to show: -- the status of Container rollouts - links from Workers to their associated Containers ### Co-locating Durable Objects and Containers @@ -71,6 +70,6 @@ There are several areas where we wish to gather feedback from users: - Do you want to integrate Containers with any other Cloudflare services? If so, which ones and how? - Do you want more ways to interact with a Container via Workers? If so, how? - Do you need different mechanisms for routing requests to containers? -- Do you need different mechanisms for scaling containers? (see [scaling documentation](/containers/scaling-and-routing) for information on autoscaling plans) +- Do you need different mechanisms for scaling containers? (see [scaling documentation](/containers/platform-details/scaling-and-routing) for information on autoscaling plans) At any point during the Beta, feel free to [give feedback using this form](https://forms.gle/CscdaEGuw5Hb6H2s7). diff --git a/src/content/docs/containers/container-package.mdx b/src/content/docs/containers/container-package.mdx index a89bfe78e0e7f32..3272daa45e516ab 100644 --- a/src/content/docs/containers/container-package.mdx +++ b/src/content/docs/containers/container-package.mdx @@ -6,7 +6,7 @@ sidebar: --- When writing code that interacts with a container instance, you can either use a -Durable Object directly or use the [`Container` module](https://github.com/cloudflare/containers) +[Durable Object directly](/containers/platform-details/durable-object-methods) or use the [`Container` module](https://github.com/cloudflare/containers) importable from [`@cloudflare/containers`](https://www.npmjs.com/package/@cloudflare/containers). ```javascript diff --git a/src/content/docs/containers/examples/container-backend.mdx b/src/content/docs/containers/examples/container-backend.mdx index a2c044927b4c2d1..ff053117dc8b30d 100644 --- a/src/content/docs/containers/examples/container-backend.mdx +++ b/src/content/docs/containers/examples/container-backend.mdx @@ -1,5 +1,4 @@ --- - summary: A simple frontend app with a containerized backend pcx_content_type: example title: Static Frontend, Container Backend @@ -166,7 +165,7 @@ select of of N instances of a Container to route requests to. In the future, we will provide improved latency-aware load balancing and autoscaling. This will make scaling stateless instances simple and routing more efficient. See the -[autoscaling documentation](/containers/scaling-and-routing) for more details. +[autoscaling documentation](/containers/platform-details/scaling-and-routing) for more details. ::: ## Define a backend container @@ -186,11 +185,11 @@ import ( ) func handler(w http.ResponseWriter, r \*http.Request) { - widgets := []map[string]interface{}{ - {"id": 1, "name": "Widget A"}, - {"id": 2, "name": "Sprocket B"}, - {"id": 3, "name": "Gear C"}, - } +widgets := []map[string]interface{}{ +{"id": 1, "name": "Widget A"}, +{"id": 2, "name": "Sprocket B"}, +{"id": 3, "name": "Gear C"}, +} w.Header().Set("Content-Type", "application/json") w.Header().Set("Access-Control-Allow-Origin", "*") @@ -199,9 +198,10 @@ func handler(w http.ResponseWriter, r \*http.Request) { } func main() { - http.HandleFunc("/api/widgets", handler) - log.Fatal(http.ListenAndServe(":8080", nil)) +http.HandleFunc("/api/widgets", handler) +log.Fatal(http.ListenAndServe(":8080", nil)) } ``` +``` diff --git a/src/content/docs/containers/examples/cron.mdx b/src/content/docs/containers/examples/cron.mdx index fc2b9e593e11228..ac982fedd20994c 100644 --- a/src/content/docs/containers/examples/cron.mdx +++ b/src/content/docs/containers/examples/cron.mdx @@ -1,5 +1,4 @@ --- - summary: Running a container on a schedule using Cron Triggers pcx_content_type: example title: Cron Container @@ -43,9 +42,7 @@ Use a cron expression in your Wrangler config to specify the schedule: }, "migrations": [ { - "new_sqlite_classes": [ - "CronContainer" - ], + "new_sqlite_classes": ["CronContainer"], "tag": "v1" } ] @@ -61,7 +58,6 @@ import { Container, getContainer } from "@cloudflare/containers"; export class CronContainer extends Container { sleepAfter = "5m"; - manualStart = true; } export default { @@ -71,13 +67,16 @@ export default { ); }, + // scheduled is called when a cron trigger fires async scheduled( _controller: any, env: { CRON_CONTAINER: DurableObjectNamespace }, ) { - await getContainer(env.CRON_CONTAINER).startContainer({ - envVars: { - MESSAGE: "Start Time: " + new Date().toISOString(), + await getContainer(env.CRON_CONTAINER).startAndWaitForPorts({ + startOptions: { + envVars: { + MESSAGE: "Start Time: " + new Date().toISOString(), + }, }, }); }, 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 36c46d0c942dab1..cba3012c5b039a2 100644 --- a/src/content/docs/containers/examples/env-vars-and-secrets.mdx +++ b/src/content/docs/containers/examples/env-vars-and-secrets.mdx @@ -10,7 +10,7 @@ description: Pass in environment variables and secrets to your container import { WranglerConfig, PackageManagers } from "~/components"; Environment variables can be passed into a Container using the `envVars` field -in the `Container` class, or by setting manually when the Container starts. +in the [`Container`](/containers/container-package) class, or by setting manually when the Container starts. 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 @@ -111,14 +111,13 @@ set as environment variables when it launches. But what if you want to set environment variables on a per-instance basis? -In this case, set `manualStart` then use the `start` method to pass in environment variables for each instance. +In this case, use the `startAndWaitForPorts()` method to pass in environment variables for each instance. We'll assume that we've set additional secrets in the Secret Store. ```js export class MyContainer extends Container { defaultPort = 8080; sleepAfter = "10s"; - manualStart = true; } export default { @@ -129,19 +128,23 @@ export default { // Each instance gets a different set of environment variables - await instanceOne.start({ - envVars: { - ACCOUNT_NAME: env.ACCOUNT_NAME + "-1", - ACCOUNT_API_KEY: env.SECRET_STORE.ACCOUNT_API_KEY_ONE, - CONTAINER_SECRET_KEY: env.CONTAINER_SECRET_KEY_TWO, + await instanceOne.startAndWaitForPorts({ + startOptions: { + envVars: { + ACCOUNT_NAME: env.ACCOUNT_NAME + "-1", + ACCOUNT_API_KEY: env.SECRET_STORE.ACCOUNT_API_KEY_ONE, + CONTAINER_SECRET_KEY: env.CONTAINER_SECRET_KEY_TWO, + }, }, }); - await instanceTwo.start({ - envVars: { - ACCOUNT_NAME: env.ACCOUNT_NAME + "-2", - ACCOUNT_API_KEY: env.SECRET_STORE.ACCOUNT_API_KEY_TWO, - CONTAINER_SECRET_KEY: env.CONTAINER_SECRET_KEY_TWO, + await instanceTwo.startAndWaitForPorts({ + startOptions: { + envVars: { + ACCOUNT_NAME: env.ACCOUNT_NAME + "-2", + ACCOUNT_API_KEY: env.SECRET_STORE.ACCOUNT_API_KEY_TWO, + CONTAINER_SECRET_KEY: env.CONTAINER_SECRET_KEY_TWO, + }, }, }); return new Response("Container instances launched"); @@ -151,3 +154,7 @@ 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/stateless.mdx b/src/content/docs/containers/examples/stateless.mdx index 9ff6a3249a556e6..f9f282405dfe8ac 100644 --- a/src/content/docs/containers/examples/stateless.mdx +++ b/src/content/docs/containers/examples/stateless.mdx @@ -1,5 +1,4 @@ --- - summary: Run multiple instances across Cloudflare's network pcx_content_type: example title: Stateless Instances @@ -36,5 +35,5 @@ select of of N instances of a Container to route requests to. In the future, we will provide improved latency-aware load balancing and autoscaling. This will make scaling stateless instances simple and routing more efficient. See the -[autoscaling documentation](/containers/scaling-and-routing) for more details. +[autoscaling documentation](/containers/platform-details/scaling-and-routing) for more details. ::: diff --git a/src/content/docs/containers/faq.mdx b/src/content/docs/containers/faq.mdx index ab8023b69472fbf..7aec2e5e4c7f70d 100644 --- a/src/content/docs/containers/faq.mdx +++ b/src/content/docs/containers/faq.mdx @@ -68,7 +68,7 @@ See [rollout documentation](/containers/platform-details/rollouts/) for details. ## How does scaling work? -See [scaling & routing documentation](/containers/scaling-and-routing/) for details. +See [scaling & routing documentation](/containers/platform-details/scaling-and-routing/) for details. ## What are cold starts? How fast are they? @@ -85,7 +85,7 @@ on image size and code execution time, among other factors. ## How do I use an existing container image? -See [image management documentation](/containers/image-management/#using-existing-images) for details. +See [image management documentation](/containers/platform-details/image-management/#using-existing-images) for details. ## Is disk persistent? What happens to my disk when my container sleeps? diff --git a/src/content/docs/containers/get-started.mdx b/src/content/docs/containers/get-started.mdx index e31c7b0e375a986..d8f679e99e80e42 100644 --- a/src/content/docs/containers/get-started.mdx +++ b/src/content/docs/containers/get-started.mdx @@ -17,7 +17,7 @@ This example Worker should give you a sense for simple Container use, and provid ### Ensure Docker is running locally In this guide, we will build and push a container image alongside your Worker code. By default, this process uses -[Docker](https://www.docker.com/) to do so. You must have Docker running locally when you run `wrangler deploy`. For most people, the best way to install Docker is to follow the [docs for installing Docker Desktop](https://docs.docker.com/desktop/). +[Docker](https://www.docker.com/) to do so. You must have Docker running locally when you run `wrangler deploy`. For most people, the best way to install Docker is to follow the [docs for installing Docker Desktop](https://docs.docker.com/desktop/). Other tools like [Colima](https://github.com/abiosoft/colima) may also work. You can check that Docker is running properly by running the `docker info` command in your terminal. If Docker is running, the command will succeed. If Docker is not running, the `docker info` command will hang or return an error including the message "Cannot connect to the Docker daemon". @@ -29,9 +29,11 @@ the `docker info` command will hang or return an error including the message "Ca Run the following command to create and deploy a new Worker with a container, from the starter template: -```sh -npm create cloudflare@latest -- --template=cloudflare/templates/containers-template -``` + When you want to deploy a code change to either the Worker or Container code, you can run the following command using [Wrangler CLI](/workers/wrangler/): @@ -40,7 +42,7 @@ When you want to deploy a code change to either the Worker or Container code, yo When you run `wrangler deploy`, the following things happen: - Wrangler builds your container image using Docker. -- Wrangler pushes your image to a [Container Image Registry](/containers/image-management/) that is automatically +- Wrangler pushes your image to a [Container Image Registry](/containers/platform-details/image-management/) that is automatically integrated with your Cloudflare account. - Wrangler deploys your Worker, and configures Cloudflare's network to be ready to spawn instances of your container @@ -67,7 +69,7 @@ And see images deployed to the Cloudflare Registry with the following command: Now, open the URL for your Worker. It should look something like `https://hello-containers.YOUR_ACCOUNT_NAME.workers.dev`. -If you make requests to the paths `/container/1` or `/container/2`, these requests are routed to specific containers. +If you make requests to the paths `/container/1` or `/container/2`, your Worker routes requests to specific containers. Each different path after "/container/" routes to a unique container. If you make requests to `/lb`, you will load balanace requests to one of 3 containers chosen at random. diff --git a/src/content/docs/containers/index.mdx b/src/content/docs/containers/index.mdx index d44f19633e77418..c72f2a74907996a 100644 --- a/src/content/docs/containers/index.mdx +++ b/src/content/docs/containers/index.mdx @@ -45,64 +45,73 @@ With Containers you can run: - Applications and libraries that require a full filesystem, specific runtime, or Linux-like environment - Existing applications and tools that have been distributed as container images -Container instances are spun up on-demand and controlled by code you write in your [Worker](/workers). Instead of chaining together API calls or writing Kubernetes operators, you just write JavaScript: +Container instances are spun up on-demand and controlled by code you write in your [Worker](/workers). Instead of chaining together API calls or writing Kubernetes operators, you just write JavaScript: ```js import { Container, getContainer } from "@cloudflare/containers"; - export class MyContainer extends Container { - defaultPort = 4000; // Port the container is listening on - sleepAfter = "10m"; // Stop the instance if requests not sent for 10 minutes - } - - export default { - async fetch(request, env) { - const { "session-id": sessionId } = await request.json(); - // Get the container instance for the given session ID - const containerInstance = getContainer(env.MY_CONTAINER, sessionId); - // Pass the request to the container instance on its default port - return containerInstance.fetch(request); - }, - }; - ``` - - - - ```json - { - "name": "container-starter", - "main": "src/index.js", - "compatibility_date": "$today", - "containers": [ - { - "class_name": "MyContainer", - "image": "./Dockerfile", - "max_instances": 5 - } - ], - "durable_objects": { - "bindings": [ - { - "class_name": "MyContainer", - "name": "MY_CONTAINER" - } - ] - }, - "migrations": [ - { - "new_sqlite_classes": ["MyContainer"], - "tag": "v1" - } - ] - } - ``` - - + export class MyContainer extends Container { + defaultPort = 4000; // Port the container is listening on + sleepAfter = "10m"; // Stop the instance if requests not sent for 10 minutes + } + + export default { + async fetch(request, env) { + const { "session-id": sessionId } = await request.json(); + // Get the container instance for the given session ID + const containerInstance = getContainer(env.MY_CONTAINER, sessionId); + // Pass the request to the container instance on its default port + return containerInstance.fetch(request); + }, + }; + ``` + + + + ```json + { + "name": "container-starter", + "main": "src/index.js", + "compatibility_date": "$today", + "containers": [ + { + "class_name": "MyContainer", + "image": "./Dockerfile", + "max_instances": 5 + } + ], + "durable_objects": { + "bindings": [ + { + "class_name": "MyContainer", + "name": "MY_CONTAINER" + } + ] + }, + "migrations": [ + { + "new_sqlite_classes": ["MyContainer"], + "tag": "v1" + } + ] + } + ``` + + + -Get started Containers dashboard + + Get started + + + Containers dashboard + --- @@ -144,7 +153,11 @@ regional placement, Workflow and Queue integrations, AI-generated code execution containers with Wrangler. - + Learn about what limits Containers have and how to work within them. @@ -158,4 +171,3 @@ regional placement, Workflow and Queue integrations, AI-generated code execution - diff --git a/src/content/docs/containers/local-dev.mdx b/src/content/docs/containers/local-dev.mdx index 9a028c02ab29199..bb2512c4445d6bb 100644 --- a/src/content/docs/containers/local-dev.mdx +++ b/src/content/docs/containers/local-dev.mdx @@ -5,7 +5,7 @@ sidebar: order: 6 --- -You can run both your container and your Worker locally, without additional configuration, by running [`npx wrangler dev`](/workers/wrangler/commands/#dev) (or `vite dev` for Vite projects using the [Cloudflare Vite plugin](/workers/vite-plugin/)) in your project's directory. +You can run both your container and your Worker locally by simply running [`npx wrangler dev`](/workers/wrangler/commands/#dev) (or `vite dev` for Vite projects using the [Cloudflare Vite plugin](/workers/vite-plugin/)) in your project's directory. To develop Container-enabled Workers locally, you will need to first ensure that a Docker compatible CLI tool and Engine are installed. For instance, you could use [Docker Desktop](https://docs.docker.com/desktop/) or [Colima](https://github.com/abiosoft/colima). @@ -13,11 +13,15 @@ Docker compatible CLI tool and Engine are installed. For instance, you could use When you start a dev session, your container image will be built or downloaded. If your [Wrangler configuration](/workers/wrangler/configuration/#containers) sets the `image` attribute to a local path, the image will be built using the local Dockerfile. -If the `image` attribute is set to a URL, the image will be pulled from the associated registry. +If the `image` attribute is set to a URL, the image will be pulled from the Cloudflare registry. + +:::note +Currently, the Cloudflare Vite-plugin does not support registry links in local development, unlike `wrangler dev`. +As a workaround, you can create a minimal Dockerfile that uses `FROM `. Make sure to `EXPOSE` a port for local dev as well. +::: Container instances will be launched locally when your Worker code calls to create -a new container. This may happen when calling `.get()` on a `Container` instance or -by calling `start()` if `manualStart` is set to `true`. Requests will then automatically be routed to the correct locally-running container. +a new container. Requests will then automatically be routed to the correct locally-running container. When the dev session ends, all associated container instances should be stopped, but local images are not removed, so that they can be reused in subsequent builds. @@ -25,10 +29,11 @@ local images are not removed, so that they can be reused in subsequent builds. :::note If your Worker app creates many container instances, your local machine may not be able to run as many containers concurrently as is possible when you deploy to Cloudflare. +Also, `max_instances` configuration option does not apply during local development. + Additionally, if you regularly rebuild containers locally, you may want to clear out old container images (using `docker image prune` or similar) to reduce disk used. -Also note that the `max_instances` configuration option is only enforced when running in production on Cloudflare's network. This limit does not apply during local development, so you may run more instances than specified. ::: ## Iterating on Container code diff --git a/src/content/docs/containers/platform-details/architecture.mdx b/src/content/docs/containers/platform-details/architecture.mdx index e7c9b48987027bd..732c2571c9cbf2c 100644 --- a/src/content/docs/containers/platform-details/architecture.mdx +++ b/src/content/docs/containers/platform-details/architecture.mdx @@ -81,7 +81,7 @@ This location will again be the nearest location to the originating request with Each container instance runs inside its own VM, which provides strong isolation from other workloads running on Cloudflare's network. Containers should be built for the `linux/amd64` architecture, and should stay within -[size limits](/containers/platform-details/limits). [Logging](containers/faq/#how-do-container-logs-work), metrics collection, and +[size limits](/containers/platform-details/limits). [Logging](/containers/faq/#how-do-container-logs-work), metrics collection, and networking are automatically set up on each container, if configured by the developer. ### After a request diff --git a/src/content/docs/containers/platform-details/environment-variables.mdx b/src/content/docs/containers/platform-details/environment-variables.mdx index 4b62e051d25430b..69291aac85cd729 100644 --- a/src/content/docs/containers/platform-details/environment-variables.mdx +++ b/src/content/docs/containers/platform-details/environment-variables.mdx @@ -19,9 +19,11 @@ The container runtime automatically sets the following variables: - `CLOUDFLARE_REGION` - a region name - `CLOUDFLARE_DURABLE_OBJECT_ID` - the ID of the Durable Object that the container is bound to -## Custom environment variables +## User-defined environment variables -Custom environment variables can be set when defining a Container in your Worker: +You can set environment variables when defining a Container in your Worker, or when starting a container instance. + +For example: ```javascript class MyContainer extends Container { @@ -33,6 +35,4 @@ class MyContainer extends Container { } ``` -:::note -If you supply environment variables with the same names, supplied values will override predefined values. -::: +More details about defining environment variables and secrets can be found in [this example](/containers/examples/env-vars-and-secrets). diff --git a/src/content/docs/containers/platform-details/image-management.mdx b/src/content/docs/containers/platform-details/image-management.mdx index 70b124d6cdf5796..2c392b97ad4de7d 100644 --- a/src/content/docs/containers/platform-details/image-management.mdx +++ b/src/content/docs/containers/platform-details/image-management.mdx @@ -11,8 +11,7 @@ import { WranglerConfig, PackageManagers } from "~/components"; ## Pushing images during `wrangler deploy` -When running `wrangler deploy`, if you set the `image` attribute in you [Wrangler configuration](/workers/wrangler/configuration/#containers) -file to a path, wrangler will build your container image locally using Docker, then push it to a registry run by Cloudflare. +When running `wrangler deploy`, if you set the `image` attribute in your [Wrangler configuration](/workers/wrangler/configuration/#containers) to a path to a Dockerfile, Wrangler will build your container image locally using Docker, then push it to a registry run by Cloudflare. This registry is integrated with your Cloudflare account and is backed by [R2](/r2/). All authentication is handled automatically by Cloudflare both when pushing and pulling images. @@ -33,16 +32,30 @@ Just provide the path to your Dockerfile: And deploy your Worker with `wrangler deploy`. No other image management is necessary. -On subsequent deploys, Wrangler will only push image layers that have changed, which saves space and time on `wrangler deploy` -calls after the initial deploy. +On subsequent deploys, Wrangler will only push image layers that have changed, which saves space and time. :::note Docker or a Docker-compatible CLI tool must be running for Wrangler to build and push images. +This is not necessary if you are using a pre-built image, as described below. ::: ## Using pre-built container images -If you wish to use a pre-built image, first, push it to the Cloudflare Registry: +Currently, all images must use `registry.cloudflare.com`. + +:::note +We plan to allow other image registries. Cloudflare will download your image, optionally using auth credentials, +then cache it globally in the Cloudflare Registry. + +This is not yet available. +::: + +If you wish to use a pre-built image, first, make sure it exists locally, then push it to the Cloudflare Registry: + +``` +docker pull +docker tag : +``` Wrangler provides a command to push images to the Cloudflare Registry: @@ -52,7 +65,7 @@ Wrangler provides a command to push images to the Cloudflare Registry: args="containers push :" /> -Additionally, you can use the `-p` flag with `wrangler containers build` to build and push an image in one step: +Or, you can use the `-p` flag with `wrangler containers build` to build and push an image in one step: -Then you can specify the URL in the image attribute: +This will output an image registry URI that you can then use in your Wrangler configuration: @@ -75,21 +88,9 @@ Then you can specify the URL in the image attribute: -Currently, all images must use `registry.cloudflare.com`, which is the default registry for Wrangler. - -To use an existing image from another repo, you can pull it, tag it, then push it to the Cloudflare Registry: - -```bash -docker pull -docker tag : -wrangler containers push : -``` - :::note -We plan to allow configuring public images directly in Wrangler config. Cloudflare will -download your image, optionally using auth credentials, then cache it globally in the Cloudflare Registry. - -This is not yet available. +Currently, the Cloudflare Vite-plugin does not support registry links in local development, unlike `wrangler dev`. +As a workaround, you can create a minimal Dockerfile that uses `FROM `. Make sure to `EXPOSE` a port in local dev as well. ::: ## Pushing images with CI diff --git a/src/content/docs/containers/platform-details/scaling-and-routing.mdx b/src/content/docs/containers/platform-details/scaling-and-routing.mdx index 83026942ab13669..0d1a6f8606af64c 100644 --- a/src/content/docs/containers/platform-details/scaling-and-routing.mdx +++ b/src/content/docs/containers/platform-details/scaling-and-routing.mdx @@ -7,15 +7,24 @@ sidebar: ### Scaling container instances with `get()` -Currently, Containers are only scaled manually by calling `BINDING.get()` with a unique ID, then -starting the container. Unless `manualStart` is set to `true` on the Container class, each -instance will start when `get()` is called. +:::note +This section uses helpers from the [Container package](/containers/container-package). +::: -``` -// gets 3 container instances -env.MY_CONTAINER.get(idOne) -env.MY_CONTAINER.get(idTwo) -env.MY_CONTAINER.get(idThree) +Currently, Containers are only scaled manually by getting containers with a unique ID, then +starting the container. Note that that getting a container does not automatically start it. + +```typescript +// get and start two container instances +const containerOne = getContainer( + env.MY_CONTAINER, + idOne, +).startAndWaitForPorts(); + +const containerTwo = getContainer( + env.MY_CONTAINER, + idTwo, +).startAndWaitForPorts(); ``` Each instance will run until its `sleepAfter` time has elapsed, or until it is manually stopped. From 0295f3eebfad249db57b093ef81b56e4962e61cd Mon Sep 17 00:00:00 2001 From: emily-shen Date: Thu, 11 Sep 2025 16:55:03 +0100 Subject: [PATCH 5/8] pr feedback and fixups --- .../docs/containers/container-package.mdx | 23 +++++++++++++----- .../containers/examples/container-backend.mdx | 24 ++++++++++--------- .../docs/containers/examples/status-hooks.mdx | 3 +-- .../docs/containers/examples/websocket.mdx | 8 +++---- .../platform-details/architecture.mdx | 20 +++++++++------- .../environment-variables.mdx | 4 +--- .../platform-details/image-management.mdx | 2 +- .../docs/workers/wrangler/configuration.mdx | 2 ++ .../account-permissions-table.mdx | 2 ++ 9 files changed, 52 insertions(+), 36 deletions(-) diff --git a/src/content/docs/containers/container-package.mdx b/src/content/docs/containers/container-package.mdx index 3272daa45e516ab..147ae4ebb8a4e7d 100644 --- a/src/content/docs/containers/container-package.mdx +++ b/src/content/docs/containers/container-package.mdx @@ -5,10 +5,18 @@ sidebar: order: 5 --- +import { PackageManagers } from "~/components"; + When writing code that interacts with a container instance, you can either use a -[Durable Object directly](/containers/platform-details/durable-object-methods) or use the [`Container` module](https://github.com/cloudflare/containers) +[Durable Object directly](/containers/platform-details/durable-object-methods) or use the [`Container` class](https://github.com/cloudflare/containers) importable from [`@cloudflare/containers`](https://www.npmjs.com/package/@cloudflare/containers). +We recommend using the `Container` class for most use cases. + + + +Then, you can define a class that extends `Container`, and use it in your Worker: + ```javascript import { Container } from "@cloudflare/containers"; @@ -16,13 +24,16 @@ class MyContainer extends Container { defaultPort = 8080; sleepAfter = "5m"; } -``` - -We recommend using the `Container` class for most use cases. -Install it with `npm install @cloudflare/containers`. +export default { + async fetch(request, env) { + // gets default instance and forwards request from outside Worker + return env.MY_CONTAINER.getByName("hello").fetch(request); + }, +}; +``` -The `Container` class extends `DurableObject` so all Durable Object functionality is available. +The `Container` class extends `DurableObject` so all [Durable Object](/durable-objects) functionality is available. It also provides additional functionality and a nice interface for common container behaviors, such as: diff --git a/src/content/docs/containers/examples/container-backend.mdx b/src/content/docs/containers/examples/container-backend.mdx index ff053117dc8b30d..2a50097d134f8cf 100644 --- a/src/content/docs/containers/examples/container-backend.mdx +++ b/src/content/docs/containers/examples/container-backend.mdx @@ -32,6 +32,7 @@ For a full example, see the [Static Frontend + Container Backend Template](https { "class_name": "Backend", "image": "./Dockerfile", + "max_instances": 3 } ], "durable_objects": { @@ -175,6 +176,7 @@ Your container should be able to handle requests to `/api/widgets`. In this case, we'll use a simple Golang backend that returns a hard-coded list of widgets.
+ ```go package main @@ -185,23 +187,23 @@ import ( ) func handler(w http.ResponseWriter, r \*http.Request) { -widgets := []map[string]interface{}{ -{"id": 1, "name": "Widget A"}, -{"id": 2, "name": "Sprocket B"}, -{"id": 3, "name": "Gear C"}, -} + widgets := []map[string]interface{}{ + {"id": 1, "name": "Widget A"}, + {"id": 2, "name": "Sprocket B"}, + {"id": 3, "name": "Gear C"}, + } - w.Header().Set("Content-Type", "application/json") - w.Header().Set("Access-Control-Allow-Origin", "*") - json.NewEncoder(w).Encode(widgets) + w.Header().Set("Content-Type", "application/json") + w.Header().Set("Access-Control-Allow-Origin", "*") + json.NewEncoder(w).Encode(widgets) } func main() { -http.HandleFunc("/api/widgets", handler) -log.Fatal(http.ListenAndServe(":8080", nil)) + http.HandleFunc("/api/widgets", handler) + log.Fatal(http.ListenAndServe(":8080", nil)) } ``` +
-``` diff --git a/src/content/docs/containers/examples/status-hooks.mdx b/src/content/docs/containers/examples/status-hooks.mdx index d71385bca9a9eef..bd8143cc5b90c8a 100644 --- a/src/content/docs/containers/examples/status-hooks.mdx +++ b/src/content/docs/containers/examples/status-hooks.mdx @@ -1,5 +1,4 @@ --- - summary: Execute Workers code in reaction to Container status changes pcx_content_type: example title: Status Hooks @@ -9,7 +8,7 @@ description: Execute Workers code in reaction to Container status changes --- When a Container starts, stops, and errors, it can trigger code execution in a Worker -that has defined status hooks on the `Container` class. +that has defined status hooks on the `Container` class. Refer to the [Container package docs](https://github.com/cloudflare/containers/blob/main/README.md#lifecycle-hooks) for more details. ```js import { Container } from '@cloudflare/containers'; diff --git a/src/content/docs/containers/examples/websocket.mdx b/src/content/docs/containers/examples/websocket.mdx index 3add1eeb9b37d37..be17b092a0b22af 100644 --- a/src/content/docs/containers/examples/websocket.mdx +++ b/src/content/docs/containers/examples/websocket.mdx @@ -1,5 +1,4 @@ --- - summary: Forwarding a Websocket request to a Container pcx_content_type: example title: Websocket to Container @@ -8,11 +7,11 @@ sidebar: description: Forwarding a Websocket request to a Container --- -WebSocket requests are automatically forwarded to a container using the default`fetch` +WebSocket requests are automatically forwarded to a container using the default `fetch` method on the `Container` class: ```js -import { Container, getContainer } from "@cloudflare/workers-types"; +import { Container, getContainer } from "@cloudflare/containers"; export class MyContainer extends Container { defaultPort = 8080; @@ -27,6 +26,5 @@ export default { }; ``` -Additionally, the `containerFetch` method can be used to forward WebSocket requests as well. - +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/platform-details/architecture.mdx b/src/content/docs/containers/platform-details/architecture.mdx index 732c2571c9cbf2c..fb3334993afcb83 100644 --- a/src/content/docs/containers/platform-details/architecture.mdx +++ b/src/content/docs/containers/platform-details/architecture.mdx @@ -25,7 +25,7 @@ by a datacenter in a location with the best latency between itself and the reque A different datacenter may be selected to optimize overall latency, if [Smart Placement](/workers/configuration/smart-placement/) is on, or if the nearest location is under heavy load. -Because all Container requests are passed through a Worker, end-users cannot make TCP or +Because all Container requests are passed through a Worker, end-users cannot make non-HTTP TCP or UDP requests to a Container instance. If you have a use case that requires inbound TCP or UDP from an end-user, please [let us know](https://forms.gle/AGSq54VvUje6kmKu8). @@ -81,25 +81,29 @@ This location will again be the nearest location to the originating request with Each container instance runs inside its own VM, which provides strong isolation from other workloads running on Cloudflare's network. Containers should be built for the `linux/amd64` architecture, and should stay within -[size limits](/containers/platform-details/limits). [Logging](/containers/faq/#how-do-container-logs-work), metrics collection, and -networking are automatically set up on each container, if configured by the developer. +[size limits](/containers/platform-details/limits). -### After a request +[Logging](/containers/faq/#how-do-container-logs-work), metrics collection, and +[networking](/containers/faq/#how-do-i-allow-or-disallow-egress-from-my-container) are automatically set up on each container, as configured by the developer. + +### Container shutdown Cloudflare will not actively shut off a container instance after a specific amount of -time. If you do not set `sleepAfter` on your Container class, or stop the instance +time. If you do not set [`sleepAfter`](https://github.com/cloudflare/containers/blob/main/README.md#properties) on your Container class, or stop the instance manually, it will continue to run unless its host server is restarted. This happens on an irregular cadence, but frequently enough where Cloudflare does not guarantee that any instance will run for any set period of time. +You can manually shutdown a container instance by calling `stop()` or `destroy()` on it - refer to the [Container package docs](https://github.com/cloudflare/containers/blob/main/README.md#container-methods) for more details. + When a container instance is going to be shut down, it is sent a `SIGTERM` signal, and then a `SIGKILL` signal after 15 minutes. You should perform any necessary -cleanup to ensure a graceful shutdown in this time. The container instance -will be rebooted elsewhere shortly after this. +cleanup to ensure a graceful shutdown in this time. + +#### Persistent disk All disk is ephemeral. When a Container instance goes to sleep, the next time it is started, it will have a fresh disk as defined by its container image. - Persistent disk is something the Cloudflare team is exploring in the future, but is not slated for the near term. diff --git a/src/content/docs/containers/platform-details/environment-variables.mdx b/src/content/docs/containers/platform-details/environment-variables.mdx index 69291aac85cd729..d15a49f4023117f 100644 --- a/src/content/docs/containers/platform-details/environment-variables.mdx +++ b/src/content/docs/containers/platform-details/environment-variables.mdx @@ -13,11 +13,9 @@ The container runtime automatically sets the following variables: - `CLOUDFLARE_APPLICATION_ID` - the ID of the Containers application - `CLOUDFLARE_COUNTRY_A2` - the [ISO 3166-1 Alpha 2 code](https://www.iso.org/obp/ui/#search/code/) of a country the container is placed in -- `CLOUDFLARE_DEPLOYMENT_ID` - the ID of the container instance - `CLOUDFLARE_LOCATION` - a name of a location the container is placed in -- `CLOUDFLARE_PLACEMENT_ID` - a placement ID - `CLOUDFLARE_REGION` - a region name -- `CLOUDFLARE_DURABLE_OBJECT_ID` - the ID of the Durable Object that the container is bound to +- `CLOUDFLARE_DURABLE_OBJECT_ID` - the ID of the Durable Object instance that the container is bound to ## User-defined environment variables diff --git a/src/content/docs/containers/platform-details/image-management.mdx b/src/content/docs/containers/platform-details/image-management.mdx index 2c392b97ad4de7d..cb0798195004a3e 100644 --- a/src/content/docs/containers/platform-details/image-management.mdx +++ b/src/content/docs/containers/platform-details/image-management.mdx @@ -80,7 +80,7 @@ This will output an image registry URI that you can then use in your Wrangler co ```json { "containers": { - "image": "registry.cloudflare.com/your-image:tag" + "image": "registry.cloudflare.com/your-account-id/your-image:tag" // ...rest of config... } } diff --git a/src/content/docs/workers/wrangler/configuration.mdx b/src/content/docs/workers/wrangler/configuration.mdx index 46e4651df8c2d65..be3544cc25dedcf 100644 --- a/src/content/docs/workers/wrangler/configuration.mdx +++ b/src/content/docs/workers/wrangler/configuration.mdx @@ -996,6 +996,8 @@ The following options are available: request to run a container than this number, the container request will error. You may have more Durable Objects than this number over a longer time period, but you may not have more concurrently. + - Defaults to 1. + - This value is only enforced when running in production on Cloudflare's network. This limit does not apply during local development, so you may run more instances than specified. - `name` diff --git a/src/content/partials/fundamentals/account-permissions-table.mdx b/src/content/partials/fundamentals/account-permissions-table.mdx index 46457341e12eb60..9cd444e3e3ab1d1 100644 --- a/src/content/partials/fundamentals/account-permissions-table.mdx +++ b/src/content/partials/fundamentals/account-permissions-table.mdx @@ -77,6 +77,8 @@ import { Markdown } from "~/components"; | { props.src === "dash" ? "Email Security" : "Cloud Email Security:" } {props.editWord} | Grants write access to [Email Security](/email-security/). | | Constellation Read | Grants read access to [Constellation](/constellation/). | | Constellation {props.editWord} | Grants write access to [Constellation](/constellation/). | +| Containers Read | Grants read access to [Containers](/containers/). | +| Containers {props.editWord} | Grants write access to [Containers](/containers/). | | D1 Read | Grants read access to [D1](/d1/). | | D1 {props.editWord} | Grants write access to [D1](/d1/). | | DDoS Botnet Feed Read | Grants read access to Botnet Feed reports. | From 20e15233fe258069026562c48c91fbbd187be7bd Mon Sep 17 00:00:00 2001 From: emily-shen Date: Mon, 22 Sep 2025 12:16:47 +0100 Subject: [PATCH 6/8] update CLOUDFLARE_DURABLE_OBJECT_ID env var description --- .../docs/containers/platform-details/environment-variables.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/docs/containers/platform-details/environment-variables.mdx b/src/content/docs/containers/platform-details/environment-variables.mdx index d15a49f4023117f..b26bca0e7911d8f 100644 --- a/src/content/docs/containers/platform-details/environment-variables.mdx +++ b/src/content/docs/containers/platform-details/environment-variables.mdx @@ -15,7 +15,7 @@ The container runtime automatically sets the following variables: - `CLOUDFLARE_COUNTRY_A2` - the [ISO 3166-1 Alpha 2 code](https://www.iso.org/obp/ui/#search/code/) of a country the container is placed in - `CLOUDFLARE_LOCATION` - a name of a location the container is placed in - `CLOUDFLARE_REGION` - a region name -- `CLOUDFLARE_DURABLE_OBJECT_ID` - the ID of the Durable Object instance that the container is bound to +- `CLOUDFLARE_DURABLE_OBJECT_ID` - the ID of the Durable Object instance that the container is bound to. You can use this to identify particular container instances on the dashboard. ## User-defined environment variables From f635ee30fde5cdf759b69eb8d848948a1b490523 Mon Sep 17 00:00:00 2001 From: emily-shen Date: Mon, 22 Sep 2025 16:07:09 +0100 Subject: [PATCH 7/8] update container shutdown --- .../docs/containers/platform-details/architecture.mdx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/content/docs/containers/platform-details/architecture.mdx b/src/content/docs/containers/platform-details/architecture.mdx index fb3334993afcb83..0bb461ef79dba9d 100644 --- a/src/content/docs/containers/platform-details/architecture.mdx +++ b/src/content/docs/containers/platform-details/architecture.mdx @@ -88,11 +88,9 @@ should be built for the `linux/amd64` architecture, and should stay within ### Container shutdown -Cloudflare will not actively shut off a container instance after a specific amount of -time. If you do not set [`sleepAfter`](https://github.com/cloudflare/containers/blob/main/README.md#properties) on your Container class, or stop the instance -manually, it will continue to run unless its host server is restarted. This -happens on an irregular cadence, but frequently enough where Cloudflare does not -guarantee that any instance will run for any set period of time. +If you do not set [`sleepAfter`](https://github.com/cloudflare/containers/blob/main/README.md#properties) +on your Container class, or stop the instance manually, the container will shut down soon after the container stops receiving requests. +By setting `sleepAfter`, the container will stay alive for approximately the specified duration. You can manually shutdown a container instance by calling `stop()` or `destroy()` on it - refer to the [Container package docs](https://github.com/cloudflare/containers/blob/main/README.md#container-methods) for more details. From 25af132f2dda9d5bc7893d72216ec8ad04be5e05 Mon Sep 17 00:00:00 2001 From: emily-shen Date: Mon, 22 Sep 2025 16:39:39 +0100 Subject: [PATCH 8/8] update secret and env vars page --- .../examples/env-vars-and-secrets.mdx | 47 +++++++++---------- 1 file changed, 21 insertions(+), 26 deletions(-) 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 cba3012c5b039a2..973b59f9cf1faf9 100644 --- a/src/content/docs/containers/examples/env-vars-and-secrets.mdx +++ b/src/content/docs/containers/examples/env-vars-and-secrets.mdx @@ -19,25 +19,21 @@ as environment variables. These examples show the various ways to pass in secrets and environment variables. In each, we will be passing in: -- the variable `"ACCOUNT_NAME"` as a hard-coded environment variable -- the secret `"CONTAINER_SECRET_KEY"` as a secret from Worker Secrets -- the secret `"ACCOUNT_API_KEY"` as a secret from the Secret Store +- the variable `"ENV_VAR"` as a hard-coded environment variable +- the secret `"WORKER_SECRET"` as a secret from Worker Secrets +- the secret `"SECRET_STORE_SECRET"` as a secret from the Secret Store In practice, you may just use one of the methods for storing secrets, but we will show both for completeness. ## Creating secrets -First, let's create the `"CONTAINER_SECRET_KEY"` secret in Worker Secrets: +First, let's create the `"WORKER_SECRET"` secret in Worker Secrets: - + Then, let's create a store called "demo" in the Secret Store, and add -the `"ACCOUNT_API_KEY"` secret to it: +the `"SECRET_STORE_SECRET"` secret to it: For full details on how to create secrets, see the [Workers Secrets documentation](/workers/configuration/secrets/) @@ -65,13 +61,13 @@ in Wrangler configuration. { "name": "my-container-worker", "vars": { - "ACCOUNT_NAME": "my-account" + "ENV_VAR": "my-env-var" }, "secrets_store_secrets": [ { "binding": "SECRET_STORE", "store_id": "demo", - "secret_name": "ACCOUNT_API_KEY" + "secret_name": "SECRET_STORE_SECRET" } ] // rest of the configuration... @@ -80,11 +76,11 @@ in Wrangler configuration. -Note that `"CONTAINER_SECRET_KEY"` does not need to be set, at it is automatically +Note that `"WORKER_SECRET"` does not need to be specified in the Wrangler config file, as it is automatically added to `env`. Also note that we did not configure anything specific for environment variables -or secrets in the container-related portion of wrangler configuration. +or secrets in the _container-related_ portion of the Wrangler configuration file. ## Using `envVars` on the Container class @@ -97,9 +93,9 @@ export class MyContainer extends Container { defaultPort = 8080; sleepAfter = "10s"; envVars = { - ACCOUNT_NAME: env.ACCOUNT_NAME, - ACCOUNT_API_KEY: env.SECRET_STORE.ACCOUNT_API_KEY, - CONTAINER_SECRET_KEY: env.CONTAINER_SECRET_KEY, + WORKER_SECRET: env.WORKER_SECRET, + ENV_VAR: env.ENV_VAR, + // we can't set the secret store binding as a default here, as getting the secret value is asynchronous }; } ``` @@ -112,7 +108,6 @@ 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. -We'll assume that we've set additional secrets in the Secret Store. ```js export class MyContainer extends Container { @@ -124,16 +119,16 @@ export default { async fetch(request, env) { if (new URL(request.url).pathname === "/launch-instances") { let instanceOne = env.MY_CONTAINER.getByName("foo"); - let instanceTwo = env.MY_CONTAINER.getByName("foo"); + let instanceTwo = env.MY_CONTAINER.getByName("bar"); // Each instance gets a different set of environment variables await instanceOne.startAndWaitForPorts({ startOptions: { envVars: { - ACCOUNT_NAME: env.ACCOUNT_NAME + "-1", - ACCOUNT_API_KEY: env.SECRET_STORE.ACCOUNT_API_KEY_ONE, - CONTAINER_SECRET_KEY: env.CONTAINER_SECRET_KEY_TWO, + ENV_VAR: env.ENV_VAR + "foo", + WORKER_SECRET: env.WORKER_SECRET, + SECRET_STORE_SECRET: await env.SECRET_STORE.get(), }, }, }); @@ -141,9 +136,9 @@ export default { await instanceTwo.startAndWaitForPorts({ startOptions: { envVars: { - ACCOUNT_NAME: env.ACCOUNT_NAME + "-2", - ACCOUNT_API_KEY: env.SECRET_STORE.ACCOUNT_API_KEY_TWO, - CONTAINER_SECRET_KEY: env.CONTAINER_SECRET_KEY_TWO, + ENV_VAR: env.ENV_VAR + "foo", + WORKER_SECRET: env.ANOTHER_WORKER_SECRET, + SECRET_STORE_SECRET: await env.OTHER_SECRET_STORE.get(), }, }, });