| 1 | --- |
| 2 | title: Docker |
| 3 | description: Images, containers, networks, and volumes as Stack resources, driven through your active Docker CLI context. |
| 4 | --- |
| 5 | |
| 6 | The Docker provider manages images, containers, networks, and |
| 7 | volumes by shelling out to the `docker` CLI's active context — |
| 8 | Docker Desktop, a remote or SSH context, a CI daemon. There is no |
| 9 | daemon API client and no credentials of its own, and the resources |
| 10 | live in the same Stack as your cloud resources. It is separate from |
| 11 | `Cloudflare.Container`; registry image references are the boundary |
| 12 | between Docker-managed images and cloud container platforms. |
| 13 | |
| 14 | New here? [Set up Docker](/docker/setup) first. |
| 15 | |
| 16 | ## Resources |
| 17 | |
| 18 | `RemoteImage`, `Network`, `Volume`, and `Container` compose into a |
| 19 | local service — here, Postgres with a named volume, a network |
| 20 | alias, a published port, and a healthcheck: |
| 21 | |
| 22 | ```typescript |
| 23 | export default Alchemy.Stack( |
| 24 | "DockerPostgresExample", |
| 25 | { |
| 26 | providers: Layer.merge(Docker.providers(), Alchemy.RandomProvider()), |
| 27 | state: Alchemy.localState(), |
| 28 | }, |
| 29 | Effect.gen(function* () { |
| 30 | const password = yield* Alchemy.makeRandom("PostgresPassword", { |
| 31 | bytes: 16, |
| 32 | }); |
| 33 | |
| 34 | const image = yield* Docker.RemoteImage("postgres-image", { |
| 35 | name: "postgres", |
| 36 | tag: "18-alpine", |
| 37 | alwaysPull: false, |
| 38 | }); |
| 39 | const network = yield* Docker.Network("app-network"); |
| 40 | const data = yield* Docker.Volume("postgres-data"); |
| 41 | |
| 42 | const postgres = yield* Docker.Container("postgres", { |
| 43 | name: "alchemy-example-postgres", |
| 44 | image, |
| 45 | environment: { |
| 46 | POSTGRES_DB: "app", |
| 47 | POSTGRES_USER: "alchemy", |
| 48 | POSTGRES_PASSWORD: password, |
| 49 | }, |
| 50 | ports: [{ external: 15432, internal: 5432 }], |
| 51 | volumes: [ |
| 52 | { hostPath: data.name, containerPath: "/var/lib/postgresql/data" }, |
| 53 | ], |
| 54 | networks: [{ name: network.name, aliases: ["postgres"] }], |
| 55 | healthcheck: { |
| 56 | cmd: ["CMD-SHELL", "pg_isready -U alchemy -d app"], |
| 57 | interval: "5 seconds", |
| 58 | timeout: "5 seconds", |
| 59 | retries: 10, |
| 60 | }, |
| 61 | start: true, |
| 62 | }); |
| 63 | return { container: postgres.name, hostPort: postgres.ports }; |
| 64 | }), |
| 65 | ); |
| 66 | ``` |
| 67 | |
| 68 | `Container` accepts the image resource directly, `alwaysPull: false` |
| 69 | pins the pulled tag so re-deploys are no-ops, and environment values |
| 70 | accept `Redacted` secrets — they are passed via process env, never |
| 71 | on the CLI. [Run local services](/docker/local-services) walks |
| 72 | through this composition. |
| 73 | |
| 74 | `Image` builds from a Dockerfile: |
| 75 | |
| 76 | ```typescript |
| 77 | const image = yield* Docker.Image("app", { |
| 78 | name: "my-app", |
| 79 | tag: "latest", |
| 80 | build: { |
| 81 | context: "./app", |
| 82 | dockerfile: "Dockerfile", |
| 83 | args: { NODE_ENV: "production" }, |
| 84 | }, |
| 85 | }); |
| 86 | ``` |
| 87 | |
| 88 | The build *is* the diff: `diff` runs `docker build` and compares the |
| 89 | resulting `imageId` against the last deploy, and the result is |
| 90 | memoized so plan + deploy build once — see |
| 91 | [Build and push an image](/docker/build-and-push) for tagging and |
| 92 | pushing to a registry. |
| 93 | |
| 94 | `Docker.inspectContainer(name)` reads live runtime details for any |
| 95 | container by name, including the bound host ports. |
| 96 | |
| 97 | :::caution |
| 98 | Most prop changes on `Network`, `Volume`, and `Container` are |
| 99 | **replacements** (delete-first) — the old resource is removed before |
| 100 | the new one is created. And a same-name pre-existing container, |
| 101 | network, or volume without alchemy labels surfaces as `Unowned`: the |
| 102 | deploy fails with `OwnedBySomeoneElse` until you opt in with |
| 103 | `.pipe(adopt(true))` or `--adopt`. |
| 104 | ::: |
| 105 | |
| 106 | ## Compose with your cloud |
| 107 | |
| 108 | [ECS](/aws/compute/ecs) Tasks build their image with your local |
| 109 | Docker — the AWS provider layers the same Docker service — and |
| 110 | [Cloudflare Containers](/cloudflare/compute/containers) bring your |
| 111 | own image by consuming the registry reference you push from |
| 112 | [Build and push an image](/docker/build-and-push). By contrast, |
| 113 | [MicroVMs](/aws/compute/microvms) build server-side on AWS and need |
| 114 | no local Docker at all. |
| 115 | |
| 116 | ## Reference |
| 117 | |
| 118 | [Container](/providers/docker/container) · |
| 119 | [Image](/providers/docker/image) · |
| 120 | [Network](/providers/docker/network) · |
| 121 | [RemoteImage](/providers/docker/remoteimage) · |
| 122 | [Volume](/providers/docker/volume) |