Build images
An image is built from a Dockerfile, a list of steps run in order against a build context, the directory whose files the steps can copy. Each step adds a layer. On a rebuild, the engine reuses every layer up to the first step whose inputs changed, and runs that step and every one after it.
Build from a Dockerfile
Section titled “Build from a Dockerfile”import * as Alchemy from "alchemy";import * as Docker from "@alchemy.run/docker";import * as Effect from "effect/Effect";
export default Alchemy.Stack( "MyApp", { providers: Docker.providers(), state: Alchemy.localState(), }, Effect.gen(function* () { const image = yield* Docker.Image("api", { name: "acme/api", tag: "latest", build: { context: "./api" }, });
return { ref: image.imageRef, id: image.imageId }; }),);build.context defaults to the current directory, and the Dockerfile to Dockerfile inside it. A .dockerignore in the context keeps files like node_modules out of the build. Leave out name, and Alchemy generates one from the Stack, stage and resource ID.
Choose the Dockerfile, stage and platform
Section titled “Choose the Dockerfile, stage and platform”const image = yield* Docker.Image("api", { name: "acme/api", build: { context: "./api", dockerfile: "docker/api.Dockerfile", target: "runtime", platform: "linux/amd64", args: { NODE_ENV: "production" }, },});target stops at one stage of a multi-stage Dockerfile. platform builds for another CPU, which you need when a Mac on Apple silicon builds for an amd64 server. args sets ARG values. Any other docker build flag goes in options.
Build args are saved in the image’s history, where anyone who pulls it can read them, so never pass a secret as one.
Use a secret in a build
Section titled “Use a secret in a build”A step that needs a secret, like a token for a private npm registry, takes it as a build secret:
FROM oven/bun:1WORKDIR /appCOPY package.json bun.lock ./RUN --mount=type=secret,id=NPM_TOKEN,env=NPM_TOKEN bun install --frozen-lockfileCOPY src ./srcCMD ["bun", "src/index.ts"]const image = yield* Docker.Image("api", { name: "acme/api", build: { context: "./api", secrets: { NPM_TOKEN: yield* Config.Redacted("NPM_TOKEN") }, },});The secret is there only while that one RUN step runs, as the variable NPM_TOKEN, or as a file at /run/secrets/NPM_TOKEN without env=. It never lands in a layer, the image’s history, or the docker command line.
When a deploy rebuilds
Section titled “When a deploy rebuilds”Every plan runs the build and compares the new image ID with the last deploy’s. With nothing changed, every layer comes from the cache, the build is quick, and it produces the same ID, so nothing updates. A new ID updates the image, and pushes it when it has a registry. Each deploy builds once, even though the plan builds too.
A build on a fresh machine has no layers to reuse, and reruns every step. See Build caches.
Build on another engine
Section titled “Build on another engine”context (the resource prop, not build.context) builds on another engine. The files in build.context are sent from your machine:
const image = yield* Docker.Image("api", { name: "acme/api", context: "vps", build: { context: "./api" },});The image then exists only on that engine. See Engines & contexts.
Destroying an image
Section titled “Destroying an image”Destroying the resource removes the image from the engine it was built on. A pushed image stays in its registry.
- Registries & credentials to push the image.
- Build caches for fast builds in CI.
- Images on cloud runtimes to run it on ECS, Cloudflare or Kubernetes.