Docker.Swarm reference
Service
Section titled “Service”Source:
src/Docker/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. Configuration changes replace the
service (delete-then-create); swarm tasks are stateless, so replacement is
cheap and avoids partially-applied service update drift.
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:
src/Docker/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 }],});