1---
2title: Build & push images
3description: 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
6This page is the canonical home for producing a **registry image
7reference** — the artifact that cloud container platforms consume.
8`Docker.Image` builds and pushes through your active Docker context;
9what 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
14All 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
21import * as Alchemy from "alchemy";
22import * as Docker from "alchemy/Docker";
23import * as Effect from "effect/Effect";
24
25export 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
47The `build` block maps onto `docker build`: `context` (default: the
48current 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
52ID.
53
54## When it rebuilds
55
56There is no source-hashing heuristic — `diff` literally runs
57`docker build` and compares the resulting `imageId` against the last
58deploy's output. If the build produces the same image, nothing
59changed; if it produces a new one, the resource updates. The build
60result is memoized per run (`Artifacts.cached`), so a plan followed
61by a deploy builds once, not twice. Destroying the stack only
62removes the local tag — nothing is deleted from any registry.
63
64## Push to a registry
65
66```typescript
67const 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
78Set `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
81declared. The push never runs `docker login` and never touches your
82global Docker config; credentials stay out of `argv` and your
83keychain. On success the resource's `imageRef` includes the registry
84host and `repoDigest` carries the immutable content digest.
85
86## Mirror and pin remote images
87
88```typescript
89const 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
103one, and `targetName`/`targetTag` re-tag it before pushing — the
104standard shape for mirroring a public image into your own registry,
105with the same `repoDigest` output as a built image. Set
106`alwaysPull: false` to pin whatever tag is already in the daemon
107rather than re-pulling on every deploy. Destroying the stack is a
108deliberate no-op for remote images: the pulled tag may be shared by
109unrelated stacks or developer workflows.
110
111## Hand the ref to your cloud
112
113Pass `image.imageRef` (or `repoDigest`, for an immutable pin) to
114whatever runs the container:
115[Cloudflare Containers](/cloudflare/compute/containers) accept it as
116a bring-your-own image, and an [ECS task definition](/aws/compute/ecs)
117takes it as the container image, and a
118[Kubernetes Deployment or Job](/kubernetes/workloads/images#build-images-with-dockerimage)
119takes it as `image`. The registry reference is the
120boundary — Cloudflare and AWS pull from the registry; no further
121Docker wiring is involved. A reference already in Cloudflare's
122managed registry (`registry.cloudflare.com/...`) is deployed as-is —
123the Container skips its own pull-and-push round-trip.
124
125Cloudflare Containers can also build and publish directly from their
126`context` or `dockerfile` props, with caching enabled by default in the
127application's generated repository. Set the Container's `publish.repository`
128to choose a shared destination and reuse builds across stages;
129see [Share published images across stages](/cloudflare/compute/containers#share-published-images-across-stages).
130This is a Cloudflare Container option; standalone `Docker.Image` resources
131continue to use the `registry` configuration shown above.
132
133## Where next
134
135Reference:
136
137- [Image API reference](/providers/docker/image)
138- [RemoteImage API reference](/providers/docker/remoteimage)