Skip to content

Lifecycle of a Container

Last updated View as MarkdownAgent setup

Deployment

After you deploy an application with a Container, your image is uploaded to Cloudflare's Registry 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.

Worker code goes live on deploy. Container instances update with a rollout. Refer to Deploy Containers.

Container instance lifecycle

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 -->|<code>ctx.container</code>| Container

    subgraph Container["Container instance"]
        direction TD
        Stopped["Stopped"]
        Starting["Starting<br/><code>start()</code> called"]
        Running["Running<br/>Not ready"]
        Ready["Ready<br/>Accepting traffic"]
        Stopping["Stopping<br/>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 or the higher-level Container class. To compare both options, refer to Containers APIs.

Lifecycle of a request

Client to Worker

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 is on, or if the nearest location is under heavy load.

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.

Worker to Durable Object

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. Each Durable Object instance is a globally routable isolate that can execute code and store state. This allows 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

When a Durable Object instance requests to start a new container instance, the nearest location with a pre-fetched image is selected.

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 in the 1-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.

Logging, metrics collection, and networking are automatically set up on each container, as configured by the developer.

Container shutdown

With the Durable Object Container API, call setInactivityTimeout() to let the runtime stop an inactive container. You can also stop a container with signal() or destroy().

The Container class sets sleepAfter to 10 minutes by default. Its onActivityExpired() implementation calls stop(). You can change the duration or override the hook.

When the platform is about to stop a container instance, it:

  1. Sends SIGTERM to the main process in the container.
  2. Waits up to 15 minutes for that process to exit.
  3. Sends SIGKILL if the process is still running.

Handle SIGTERM in your image if you need cleanup before exit. The same sequence runs when a rollout replaces a container instance with a new image.

Lifecycle events

The Durable Object Container API provides monitor(). Its promise resolves when the container exits and rejects when the container errors.

The Container class adds hooks that run Worker code when the container changes state:

  • onStart() — Runs after the container has started.
  • onStop() — Runs after the container process exits. Receives the exit code and reason for the stop.
  • onActivityExpired() — Runs when the 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() — Runs when the container exits with an error.

Refer to the status hooks example for a full implementation.

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.

Snapshots are coming soon, which allow the user to quickly persist and restore the disk from an entire container or a directory.

You can also use FUSE to persist disk to R2 or other object storage backends. Though you should not expect native SSD-like performance while using FUSE.

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.

Was this helpful?