How to Deploy Workerd Applications Using Containers: A Complete Guide
Workerd deploys applications inside Docker containers using a built-in ContainerClient that communicates with the Docker daemon via Unix sockets, enabling sandboxed execution with optional egress-proxy sidecars for secure outbound traffic.
Deploying workerd applications using containers allows you to run each Durable Object or service inside its own isolated Docker container while maintaining Workerd's native compatibility flags and runtime semantics. The cloudflare/workerd repository ships with a native container client that manages the full container lifecycle directly from the Workerd binary.
Understanding Workerd's Container Architecture
The container integration centers on the ContainerClient class, which implements the RPC interface used by the JSG Container object. This client translates Workerd's internal operations into Docker API calls, creating a seamless bridge between the Workerd runtime and container orchestration platforms.
The ContainerClient Implementation
The core container logic resides in two primary source files:
src/workerd/server/container-client.h– Declares theContainerClientclass that handles the RPC interface and container state management.src/workerd/server/container-client.c++– Contains the actual Docker API implementation, includingdockerApiRequestand lifecycle helpers for creating, starting, stopping, and destroying containers.
When Workerd initializes with container support enabled, it instantiates a ContainerClient that connects to the Docker daemon through a Unix socket (or TCP socket). The client exposes methods like createContainer, startContainer, and listenTcp that manage the sandboxed environment.
Configuration and API Schemas
The container feature relies on Cap'n Proto schemas for type-safe communication:
src/workerd/server/docker-api.capnp– Defines the Cap'n Proto schema for Docker HTTP API requests and responses used by the client.src/workerd/server/workerd.capnp– Contains the top-level configuration schema, including theWorker::ContainerEnginedefinition that enables the container engine block in your configuration.
Configuring the Container Engine
Workerd parses container settings during the configuration phase and wires the client into the worker's execution path.
Writing the Workerd Configuration
To enable containerized execution, add a containerEngine block to your Workerd configuration file. The following YAML maps to Worker::ContainerEngine in src/workerd/server/workerd.capnp:
services:
- name: my-app
worker:
modules:
- name: worker
esModule: embed "worker.mjs"
compatibilityDate: "2024-01-01"
containerEngine:
localDocker:
socketPath: "unix:/var/run/docker.sock"
containerEgressInterceptorImage: "cloudflare/proxy-everything:main"
The localDocker variant is currently the only supported engine. The socketPath specifies the Docker daemon socket, while containerEgressInterceptorImage optionally defines an egress-proxy sidecar image for handling outbound HTTP traffic.
Server Configuration Parsing
In src/workerd/server/server.c++ (lines 4012-4830), the Server::applyConfig method reads the containerEngineConf from the parsed configuration. When the variant is localDocker, Workerd extracts the socket path and sidecar image settings, then initializes the ContainerClient for that worker. All subsequent I/O is routed through the container's network namespace.
Building and Running Containerized Workerd Applications
Once configured, you can package Workerd itself as a container image or let Workerd spawn child containers dynamically.
Creating the Docker Image
Build a container image that includes the Workerd binary and your configuration:
FROM debian:bookworm-slim AS builder
RUN apt-get update && apt-get install -y libcapnp0.8 libkj0.9 libssl3 ca-certificates && rm -rf /var/lib/apt/lists/*
COPY workerd /usr/local/bin/workerd
COPY my-app.wd-test /etc/workerd/my-app.wd-test
ENTRYPOINT ["/usr/local/bin/workerd", "--config", "/etc/workerd/my-app.wd-test"]
Build and push the image:
docker build -t my-org/workerd-app:latest .
docker push my-org/workerd-app:latest
Running with Docker Socket Access
To allow Workerd to spawn and manage containers, mount the Docker daemon socket when running the container:
docker run -d \
--name workerd-runtime \
-v /var/run/docker.sock:/var/run/docker.sock \
-v $(pwd)/my-app.wd-test:/etc/workerd/my-app.wd-test \
my-org/workerd-app:latest
Workerd reads the containerEngine stanza, contacts the Docker daemon via the mounted socket, and creates child containers for your workers dynamically. The ContainerClient::createContainer method builds the Docker "create" request, injecting the worker's environment variables and container name.
Advanced: Egress Proxy Sidecars
For applications requiring controlled outbound access, Workerd supports running an egress-proxy sidecar container alongside the main worker container.
Configuring the Sidecar
When containerEgressInterceptorImage is specified in the configuration, ContainerClient::createSidecarContainer (lines 1500-1550 of container-client.c++) launches a second container that listens on a dynamically allocated high port. The setEgressHttp RPC method routes all outbound HTTP/HTTPS traffic through this sidecar, providing "proxy-everything" functionality required for secure sandboxed egress.
The sidecar container runs in the same network namespace as the worker, with ContainerClient::listenTcp exposing the TCP port on the host that Workerd uses for inbound connections.
Summary
- Workerd's container support is implemented through the
ContainerClientclass insrc/workerd/server/container-client.c++, which communicates directly with the Docker daemon. - Configuration occurs via the
containerEngineblock inworkerd.capnpschemas, parsed byServer::applyConfiginserver.c++. - Socket access requires mounting
/var/run/docker.sockinto the Workerd container to enable container creation and management. - Egress control is available through the optional
containerEgressInterceptorImagesetting, which launches a sidecar container viacreateSidecarContainer. - Networking is handled dynamically, with
listenTcpallocating host ports that forward traffic to the sandboxed container.
Frequently Asked Questions
How does Workerd communicate with the Docker daemon?
Workerd communicates with the Docker daemon through a Unix socket (or TCP socket) specified in the socketPath configuration field. The ContainerClient class in src/workerd/server/container-client.c++ uses this socket to issue Docker API calls for container creation, lifecycle management, and networking setup.
What is the purpose of the egress-proxy sidecar?
The egress-proxy sidecar provides controlled outbound HTTP/HTTPS access for sandboxed containers. When containerEgressInterceptorImage is configured, Workerd launches a secondary container via createSidecarContainer that intercepts all outbound traffic through the setEgressHttp RPC method, enabling secure proxy functionality without exposing the host network directly to the worker.
Can I run Workerd containers on Kubernetes?
Yes. Since Workerd's container integration uses standard Docker API calls through the container runtime interface (CRI), you can deploy Workerd on Kubernetes by ensuring the pod has access to the container runtime socket. Mount the appropriate socket path (often /var/run/docker.sock or /var/run/containerd/containerd.sock depending on your Kubernetes setup) and configure the socketPath in your Workerd configuration accordingly.
Where is the container engine configuration defined in the source code?
The container engine configuration is defined in src/workerd/server/workerd.capnp within the Worker::ContainerEngine Cap'n Proto struct. The runtime parsing logic resides in src/workerd/server/server.c++ (lines 4012-4830), where Server::applyConfig extracts the containerEngineConf and initializes the ContainerClient for workers with container support enabled.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →