workerd for a wrangler project
A wrangler-built Worker on bare workerd, with Durable Objects on local disk.
Names and addresses are placeholders. The list says what each one stands for.
What it does here
workerd is the runtime Cloudflare runs Workers on. Here it runs the yaos Worker and its two Durable Objects in a container, with their SQLite files on the data disk.
Canonical setup
wrangler bundles the Worker and uploads it to Cloudflare. workerd loads a Worker from a Cap'n Proto config file instead, and serves it with workerd serve config.capnp. Upstream: cloudflare/workerd.
In this build
- Bundle with esbuildThe same import conditions wrangler uses:
workerd,worker,browser, withcloudflare:*left external. Only the bundle is needed, not the upload. - Durable Objects with SQLEach namespace gets
enableSql = trueand its ownuniqueKey. - Durable Object storage on local disk
durableObjectStorage = (localDisk = "do-disk"), pointing at a writablediskservice backed by a directory. - Private backends as `external` servicesThe S3 store is reachable only through a service binding.
- Secrets via `fromEnvironment`They come from the container's environment, so none of them end up in the image.
- workerd pinned
1.20261019.1, about two weeks old at build time, not the release from that morning.
using Workerd = import "/workerd/workerd.capnp";
const config :Workerd.Config = (
services = [
(name = "yaos", worker = .yaosWorker),
(name = "do-disk", disk = (path = "/data/do", writable = true)),
(name = "garage", external = (address = "garage:3900", http = ())),
],
sockets = [ (name = "http", address = "*:8787", http = (), service = "yaos") ],
);
const yaosWorker :Workerd.Worker = (
modules = [ (name = "worker.js", esModule = embed "dist/worker.js") ],
compatibilityDate = "2026-03-02",
bindings = [
(name = "YAOS_SYNC", durableObjectNamespace = "VaultSyncServer"),
(name = "YAOS_CONFIG", durableObjectNamespace = "ServerConfig"),
(name = "S3", service = "garage"),
(name = "S3_ACCESS_KEY_ID", fromEnvironment = "S3_ACCESS_KEY_ID"),
(name = "S3_SECRET_ACCESS_KEY", fromEnvironment = "S3_SECRET_ACCESS_KEY"),
# ... S3_BUCKET, SYNC_TOKEN and the optional yaos flags, same pattern
],
durableObjectNamespaces = [
(className = "VaultSyncServer", uniqueKey = "yaos-vault-sync", enableSql = true),
(className = "ServerConfig", uniqueKey = "yaos-server-config", enableSql = true),
],
durableObjectStorage = (localDisk = "do-disk"),
);The Dockerfile has two stages. The build stage fetches yaos at a pinned commit, runs npm ci in server/, installs workerd from npm, and bundles. The runtime stage has only the workerd binary, the config and the bundle.
FROM node:22-slim AS build
ARG YAOS_REF=2174eb89538ce7e213e9e33a0e75ecaf1a3f1f58
ARG WORKERD_VERSION=1.20261019.1
RUN apt-get update && apt-get install -y --no-install-recommends git ca-certificates
WORKDIR /build/yaos
RUN git init -q && git remote add origin https://github.com/kavinsood/yaos \
&& git fetch -q --depth 1 origin "$YAOS_REF" && git checkout -q FETCH_HEAD
RUN cd server && npm ci --no-audit --no-fund
WORKDIR /build/shim
COPY package.json ./
RUN npm install && npm install --no-save "workerd@${WORKERD_VERSION}"
COPY entry.js r2-s3.js build.mjs config.capnp ./
RUN node build.mjs \
&& cp node_modules/@cloudflare/workerd-linux-64/bin/workerd /build/workerd
FROM debian:trixie-slim
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates
COPY --from=build /build/workerd /usr/local/bin/workerd
WORKDIR /app
COPY --from=build /build/shim/config.capnp ./config.capnp
COPY --from=build /build/shim/dist ./dist
CMD ["workerd", "serve", "/app/config.capnp", "--verbose"]Seams
Where this meets the rest of the build. Each one leads with what you will actually see.
Symptomfetch("http://garage:3900/...") fails, though the name resolves inside the container
By default workerd's global outbound only allows public addresses, and a Docker network is private. Don't widen the outbound. Give the store its own external service and call it through the binding (env.S3.fetch(...)).
Verify
The Durable Object directory holds one .sqlite file per object instance, plus workerd's metadata.sqlite.
ls /srv/data/yaos/workerd/do
docker compose logs yaos- 4 Nov 2026First written.