| 1 | --- |
| 2 | title: Build & push images |
| 3 | description: Build a container image from a Dockerfile, push it to a registry, and hand the resulting image reference to Cloudflare Containers or AWS ECS. |
| 4 | --- |
| 5 | |
| 6 | This page is the canonical home for producing a **registry image |
| 7 | reference** — the artifact that cloud container platforms consume. |
| 8 | `Docker.Image` builds and pushes through your active Docker context; |
| 9 | what comes out the other end is an `imageRef` (and, after a push, a |
| 10 | `repoDigest`) that you pass to |
| 11 | [Cloudflare Containers](/cloudflare/compute/containers) or an |
| 12 | [AWS ECS task definition](/aws/compute/ecs). |
| 13 | |
| 14 | All you need is a Docker daemon reachable from the CLI — see |
| 15 | [Setup](/docker/setup) if you haven't wired one up yet. |
| 16 | |
| 17 | ## Build from a Dockerfile |
| 18 | |
| 19 | ```typescript |
| 20 | // alchemy.run.ts |
| 21 | import * as Alchemy from "alchemy"; |
| 22 | import * as Docker from "alchemy/Docker"; |
| 23 | import * as Effect from "effect/Effect"; |
| 24 | |
| 25 | export default Alchemy.Stack( |
| 26 | "MyImages", |
| 27 | { |
| 28 | providers: Docker.providers(), |
| 29 | state: Alchemy.localState(), |
| 30 | }, |
| 31 | Effect.gen(function* () { |
| 32 | const image = yield* Docker.Image("app", { |
| 33 | name: "my-app", |
| 34 | tag: "latest", |
| 35 | build: { |
| 36 | context: "./app", |
| 37 | dockerfile: "Dockerfile", |
| 38 | args: { NODE_ENV: "production" }, |
| 39 | }, |
| 40 | }); |
| 41 | |
| 42 | return { ref: image.imageRef, id: image.imageId }; |
| 43 | }), |
| 44 | ); |
| 45 | ``` |
| 46 | |
| 47 | The `build` block maps onto `docker build`: `context` (default: the |
| 48 | current working directory), `dockerfile` (default `"Dockerfile"`), |
| 49 | `args`, a multi-stage `target`, and a `platform` such as |
| 50 | `"linux/amd64"`, plus `cacheFrom`/`cacheTo` for build caching. Omit |
| 51 | `name` and alchemy generates one from the stack, stage, and logical |
| 52 | ID. |
| 53 | |
| 54 | ## When it rebuilds |
| 55 | |
| 56 | There is no source-hashing heuristic — `diff` literally runs |
| 57 | `docker build` and compares the resulting `imageId` against the last |
| 58 | deploy's output. If the build produces the same image, nothing |
| 59 | changed; if it produces a new one, the resource updates. The build |
| 60 | result is memoized per run (`Artifacts.cached`), so a plan followed |
| 61 | by a deploy builds once, not twice. Destroying the stack only |
| 62 | removes the local tag — nothing is deleted from any registry. |
| 63 | |
| 64 | ## Push to a registry |
| 65 | |
| 66 | ```typescript |
| 67 | const image = yield* Docker.Image("app", { |
| 68 | name: "my-app", |
| 69 | build: { context: "./app" }, |
| 70 | registry: { |
| 71 | server: "ghcr.io", |
| 72 | username: "octocat", |
| 73 | password: Config.Redacted("GITHUB_TOKEN"), |
| 74 | }, |
| 75 | }); |
| 76 | ``` |
| 77 | |
| 78 | Set `registry` with the server (e.g. `ghcr.io`), a username, and a |
| 79 | `Redacted` password, and the image is pushed after every build — |
| 80 | `skipPush: true` keeps the build local while leaving the credentials |
| 81 | declared. The push never runs `docker login` and never touches your |
| 82 | global Docker config; credentials stay out of `argv` and your |
| 83 | keychain. On success the resource's `imageRef` includes the registry |
| 84 | host and `repoDigest` carries the immutable content digest. |
| 85 | |
| 86 | ## Mirror and pin remote images |
| 87 | |
| 88 | ```typescript |
| 89 | const mirrored = yield* Docker.RemoteImage("nginx-mirror", { |
| 90 | name: "nginx", |
| 91 | tag: "alpine", |
| 92 | targetName: "acme/nginx", |
| 93 | targetTag: "alpine", |
| 94 | registry: { |
| 95 | server: "ghcr.io", |
| 96 | username: "octocat", |
| 97 | password: Config.Redacted("GITHUB_TOKEN"), |
| 98 | }, |
| 99 | }); |
| 100 | ``` |
| 101 | |
| 102 | `Docker.RemoteImage` pulls an existing image instead of building |
| 103 | one, and `targetName`/`targetTag` re-tag it before pushing — the |
| 104 | standard shape for mirroring a public image into your own registry, |
| 105 | with the same `repoDigest` output as a built image. Set |
| 106 | `alwaysPull: false` to pin whatever tag is already in the daemon |
| 107 | rather than re-pulling on every deploy. Destroying the stack is a |
| 108 | deliberate no-op for remote images: the pulled tag may be shared by |
| 109 | unrelated stacks or developer workflows. |
| 110 | |
| 111 | ## Hand the ref to your cloud |
| 112 | |
| 113 | Pass `image.imageRef` (or `repoDigest`, for an immutable pin) to |
| 114 | whatever runs the container: |
| 115 | [Cloudflare Containers](/cloudflare/compute/containers) accept it as |
| 116 | a bring-your-own image, and an [ECS task definition](/aws/compute/ecs) |
| 117 | takes it as the container image, and a |
| 118 | [Kubernetes Deployment or Job](/kubernetes/workloads/images#build-images-with-dockerimage) |
| 119 | takes it as `image`. The registry reference is the |
| 120 | boundary — Cloudflare and AWS pull from the registry; no further |
| 121 | Docker wiring is involved. A reference already in Cloudflare's |
| 122 | managed registry (`registry.cloudflare.com/...`) is deployed as-is — |
| 123 | the Container skips its own pull-and-push round-trip. |
| 124 | |
| 125 | Cloudflare Containers can also build and publish directly from their |
| 126 | `context` or `dockerfile` props, with caching enabled by default in the |
| 127 | application's generated repository. Set the Container's `publish.repository` |
| 128 | to choose a shared destination and reuse builds across stages; |
| 129 | see [Share published images across stages](/cloudflare/compute/containers#share-published-images-across-stages). |
| 130 | This is a Cloudflare Container option; standalone `Docker.Image` resources |
| 131 | continue to use the `registry` configuration shown above. |
| 132 | |
| 133 | ## Where next |
| 134 | |
| 135 | Reference: |
| 136 | |
| 137 | - [Image API reference](/providers/docker/image) |
| 138 | - [RemoteImage API reference](/providers/docker/remoteimage) |