Skip to content

ContainerApplication

Source: src/Cloudflare/Container/ContainerApplication.ts

A Cloudflare Container Application — the deployed, scalable unit that runs a containerized program on Cloudflare’s compute platform. Alchemy bundles the main entrypoint, builds a Docker image, pushes it to the Cloudflare registry, and reconciles the application’s scaling and runtime configuration.

This is the lower-level resource backing the {@link Container} platform binding; in application code you typically extend Cloudflare.Container to define and bind a container to a Durable Object rather than referencing this resource directly. The same props shape (main, instanceType, instances, etc.) is accepted by the Cloudflare.Container(...) class form shown below.

Point main at the container’s entrypoint file; Alchemy bundles it and uses it as the image’s entrypoint. The application name is derived deterministically from the stack, stage, and logical ID unless you set an explicit name, and handler selects which export to run when it isn’t the default.

Minimal container

import * as Cloudflare from "alchemy/Cloudflare";
export class Sandbox extends Cloudflare.Container<Sandbox>()("Sandbox", {
main: import.meta.filename,
}) {}

The single main prop is enough to ship a container: Alchemy bundles the entrypoint, builds and pushes the image, and provisions an application with one instance. Reach for the other props only when you need to scale, expose ports, or customize the build.

Named container with a non-default handler export

export class Worker extends Cloudflare.Container<Worker>()("Worker", {
main: import.meta.filename,
handler: "runWorker",
name: "background-worker",
}) {}

name pins a stable application name (instead of the generated one), which is useful for adopting an existing application, while handler runs the named runWorker export rather than the module’s default.

By default the entrypoint is bundled for the bun runtime. Use runtime to switch to Node, external to keep native/precompiled packages out of the bundle (auto-installed in the image unless autoInstallExternals is false), a custom dockerfile as the image base, and registryId to override the registry host.

Node runtime with external native deps

export class ImageApi extends Cloudflare.Container<ImageApi>()("ImageApi", {
main: import.meta.filename,
runtime: "node",
external: ["sharp"],
autoInstallExternals: true,
}) {}

Marking sharp as external stops Rolldown from bundling the native module; because autoInstallExternals is true, Alchemy runs npm install sharp inside the image so the dependency is present at runtime.

Custom Dockerfile base and registry

export class Custom extends Cloudflare.Container<Custom>()("Custom", {
main: import.meta.filename,
dockerfile: "FROM oven/bun:1\nRUN apt-get update && apt-get install -y ffmpeg",
autoInstallExternals: false,
registryId: "registry.cloudflare.com",
}) {}

Alchemy appends the program-copy and entrypoint steps to your dockerfile, so you control the base image and any system packages; autoInstallExternals: false skips the redundant install step when your Dockerfile already provides those packages.

Control the desired and maximum instance counts with instances/maxInstances and pick a compute size with instanceType. For finer control, override vcpu, memory, and disk directly.

Autoscaling with a larger instance type

export class Sandbox extends Cloudflare.Container<Sandbox>()("Sandbox", {
main: import.meta.filename,
instanceType: "standard-1",
instances: 1,
maxInstances: 5,
}) {}

The application keeps one instance running and may scale out to five under load, each on the standard-1 size. Use a larger instanceType (or the explicit overrides below) when the default dev size is too small.

Explicit CPU, memory, and disk overrides

export class Heavy extends Cloudflare.Container<Heavy>()("Heavy", {
main: import.meta.filename,
vcpu: 2,
memory: "4GB",
disk: { size: "10GB" },
}) {}

These props override the per-instance resource allocation independently of instanceType, which is handy when a workload needs, say, extra disk for scratch space without bumping every other dimension.

Inject configuration with environmentVariables (plain values) and secrets (references to stored secrets), and override the image’s command or entrypoint. labels attach metadata to the deployment.

Environment variables, secrets, and a command override

export class Api extends Cloudflare.Container<Api>()("Api", {
main: import.meta.filename,
environmentVariables: [{ name: "LOG_LEVEL", value: "info" }],
secrets: [{ name: "API_KEY", type: "env", secret: "my-stored-secret" }],
command: ["bun", "run", "start"],
labels: [{ name: "team", value: "payments" }],
}) {}

environmentVariables are visible plain values, while secrets map a stored secret into the runtime as an env var without exposing it in config; command overrides the container’s startup command and labels tag the deployment for organization.

Passing env and selecting runtime exports

export class Job extends Cloudflare.Container<Job>()("Job", {
main: import.meta.filename,
env: { REGION: "wnam", FEATURE_FLAG: "on" },
exports: ["default"],
}) {}

env injects values into the bundled program’s runtime context (as opposed to the deployment-level environmentVariables), and exports declares which symbols from the entrypoint module the runtime should wire up.

Configure outbound/inbound networking with network and dns, expose ports, and gate readiness with checks.

export class Web extends Cloudflare.Container<Web>()("Web", {
main: import.meta.filename,
ports: [{ name: "http", port: 8080 }],
network: { assignIpv4: "predefined", mode: "public" },
dns: { servers: ["1.1.1.1"], searches: ["internal"] },
checks: [{ name: "ready", type: "http", port: "8080", tls: false }],
}) {}

ports publishes the named port the program listens on, network controls IP assignment and public/private reachability, dns overrides resolver settings, and checks tells Cloudflare how to probe the container before routing traffic to it.

Turn on log shipping with observability and install sshPublicKeyIds for interactive access to running instances.

export class Api extends Cloudflare.Container<Api>()("Api", {
main: import.meta.filename,
observability: { logs: { enabled: true } },
sshPublicKeyIds: ["ssh-key-id-123"],
}) {}

observability.logs.enabled streams the container’s logs into Cloudflare’s telemetry pipeline (queryable via the resource’s logs/tail operations), and sshPublicKeyIds authorizes the listed keys to connect to instances for debugging.

Influence where and how Cloudflare schedules instances with schedulingPolicy, constraints, and affinities.

export class Edge extends Cloudflare.Container<Edge>()("Edge", {
main: import.meta.filename,
schedulingPolicy: "regional",
constraints: { tier: 1 },
affinities: { colocation: "datacenter" },
}) {}

schedulingPolicy selects the control-plane placement strategy, constraints.tier restricts which capacity tier instances may land on, and affinities.colocation keeps related instances in the same datacenter to reduce inter-instance latency.

When an update changes the configuration, rollout controls how the new version is rolled out across instances.

export class Api extends Cloudflare.Container<Api>()("Api", {
main: import.meta.filename,
instances: 4,
maxInstances: 4,
rollout: { strategy: "rolling", stepPercentage: 25 },
}) {}

A rolling strategy with stepPercentage: 25 replaces instances in 25% increments so the application stays available during the update; the default immediate strategy swaps everything at once.