Docker.Swarm reference
Service
Section titled “Service”Source:
packages/docker/src/Service.ts
A Docker Swarm service: N replicas of a container kept alive by the swarm, deployed through the active (or a named) Docker context.
The target engine must be a swarm manager — Service wraps
docker service, swarm mode’s orchestration API. Declare the swarm with
Docker.Swarm and pass it as context so the service deploys after the
swarm exists; for a plain single container on a non-swarm daemon use
Docker.Container instead.
The service’s image comes from one of two sources:
image— run a pre-built reference (a registry ref, or aDocker.Image/Docker.RemoteImageresource).main— bundle an inline Effect program into a generated bun image, built directly against the service’s Docker context. The impl returns{ fetch }and may register background loops viaServerHost.run— the same effectful platform shape asAWS.ECS.Service.
The bundled image is content-addressed and only rebuilt when the program
(or its generated Dockerfile) changes. It is built on the target engine’s
local store — single-node swarms run it as-is; multi-node swarms need the
image on a registry every node can reach (build with Docker.Image +
registry and pass the pushed ref as image instead).
Only replicated services are supported. Changes to the image, replicas,
environment, labels, networks, ports, mounts, secrets, configs, constraints,
healthcheck, and rollout or restart policies are applied in place with
docker service update, so swarm rolls tasks over following updateConfig.
An update that swarm rolls back or pauses fails the deploy with
DockerServiceUpdateFailed. Changing the name, context, command, args,
endpoint mode, placement preferences, or read-only root filesystem, or
removing a policy setting, replaces the service (delete-then-create).
Service: Creating Services
Section titled “Service: Creating Services”Replicated Nginx
const swarm = yield* Docker.Swarm("swarm");const web = yield* Docker.Service("web", { context: swarm, image: "nginx:alpine", replicas: 3, ports: [{ external: 8080, internal: 80 }],});Run a Built Image
const image = yield* Docker.Image("app-image", { build: { context: "./app" },});const app = yield* Docker.Service("app", { context: swarm, image, replicas: 2,});Service: Effectful Services
Section titled “Service: Effectful Services”Inline Effect Server
const swarm = yield* Docker.Swarm("swarm");const api = yield* Docker.Service( "Api", { context: swarm, main: import.meta.url, port: 3000, ports: [{ external: 8080, internal: 3000 }], replicas: 2, }, Effect.gen(function* () { return { fetch: Effect.gen(function* () { return yield* HttpServerResponse.json({ ok: true }); }), }; }),);Background Loops with ServerHost
// Class props may be an Effect, so the service can yield the swarm it// deploys into (declared once at module level).const Swarm = Docker.Swarm("swarm");
export default class Worker extends Docker.Service<Worker>()( "Worker", Effect.gen(function* () { const swarm = yield* Swarm; return { context: swarm, main: import.meta.url, port: 3000 }; }), Effect.gen(function* () { const host = yield* ServerHost; yield* host.run( pollQueue.pipe(Effect.repeat(Schedule.spaced("5 seconds")), Effect.asVoid), ); return { fetch: Effect.succeed(HttpServerResponse.text("ok")), }; }),) {}Service: Bundling & Tree-shaking
Section titled “Service: Bundling & Tree-shaking”main is bundled with rolldown at deploy time. Unused code is
tree-shaken. effect, alchemy, and @distilled.cloud are marked
pure so unused parts prune more aggressively. Your app is not
marked pure.
Mark additional packages as pure
Only list packages with no top-level side effects.
{ main: import.meta.url, build: { pure: { packages: ["my-lib", "@my-scope/*"] }, },}Turn it off
{ main: import.meta.url, build: { pure: false },}Service: Docker Contexts
Section titled “Service: Docker Contexts”const vps = yield* Docker.Context("vps", { docker: "host=ssh://deploy@example.com",});const swarm = yield* Docker.Swarm("swarm", { context: vps, advertiseAddr: "10.0.0.1",});const app = yield* Docker.Service("app", { context: swarm, image: "nginx:alpine", replicas: 3,});Service: Networks & Volumes
Section titled “Service: Networks & Volumes”const network = yield* Docker.Network("app-net", { context: swarm, driver: "overlay",});const db = yield* Docker.Service("db", { context: swarm, image: "postgres:18-alpine", networks: [{ name: network.name, aliases: ["postgres"] }], volumes: [{ hostPath: "pg-data", containerPath: "/var/lib/postgresql/data" }],});Service: Rollouts & Placement
Section titled “Service: Rollouts & Placement”const app = yield* Docker.Service("app", { image: "ghcr.io/acme/app:latest", replicas: 4, updateConfig: { parallelism: 1, delay: "10s", failureAction: "rollback", order: "start-first", }, placement: { constraints: ["node.role==worker"], maxReplicasPerNode: 2, },});Service: Secrets & Configs
Section titled “Service: Secrets & Configs”const app = yield* Docker.Service("app", { image: "ghcr.io/acme/app:latest", secrets: [{ source: "db-password", target: "db_password", mode: 0o400 }], configs: [{ source: "app-config", target: "/etc/app/config.yaml" }],});Source:
packages/docker/src/Swarm.ts
Swarm mode on a Docker engine — an idempotent docker swarm init.
Turns the engine behind the given context (or the local engine) into a
single-node swarm: the node becomes a manager that can run Docker.Service
workloads. Regular (non-swarm) docker usage of the engine is unaffected.
Pass the swarm as the context of a Docker.Service or overlay
Docker.Network: the workload then deploys after the swarm exists and
inherits its Docker context.
An engine that is already in swarm mode is treated as foreign — adopt it
with adopt(true) (or --adopt) to manage it. Destroying the resource
dissolves the node’s swarm membership (docker swarm leave --force),
which stops every service running on it — services managed by the same
stack are destroyed first via their dependency edges.
Growing the cluster beyond one node is host-level setup: run
docker swarm join --token <token> <manager-ip>:2377 on each additional
machine (docker swarm join-token worker on the manager prints the
command). Alchemy manages the swarm’s workloads through the manager.
Swarm: Creating a Swarm
Section titled “Swarm: Creating a Swarm”Local single-node swarm
const swarm = yield* Docker.Swarm("swarm");Remote engine over SSH
const vps = yield* Docker.Context("vps", { docker: "host=ssh://deploy@example.com",});const swarm = yield* Docker.Swarm("swarm", { context: vps, advertiseAddr: "10.0.0.1",});Swarm: Using an Existing Swarm
Section titled “Swarm: Using an Existing Swarm”Reference without owning
// Services don't require a Swarm resource — point them at an engine that// is already a manager and the swarm's lifecycle stays external: destroy// removes the services, never the swarm.const web = yield* Docker.Service("web", { context: vps, image: "nginx:alpine",});Adopt an already-initialized engine
// Adoption makes the swarm part of the stack — destroy then dissolves// the node's membership.const swarm = yield* Docker.Swarm("swarm").pipe(adopt(true));Swarm: Deploying into the Swarm
Section titled “Swarm: Deploying into the Swarm”const swarm = yield* Docker.Swarm("swarm");const web = yield* Docker.Service("web", { context: swarm, image: "nginx:alpine", replicas: 2, ports: [{ external: 8080, internal: 80 }],});