# Custom images Build an image once with your code and dependencies, then start every sandbox from it. An image is built from any Dockerfile, from any public or private container image, or from a short recipe of packages. Each build of a name is its next version, tags such as `latest` and `prod` point at versions, and an image can say what a sandbox from it runs and when that sandbox is ready. ## Build from a Dockerfile Point the SDK or the CLI at a folder with a Dockerfile. The folder is the build context, exactly as with `docker build`: every file its `.dockerignore` leaves in is packed, and only the parts that changed since your last build are uploaded. ```bash no-run runtime image build . -t web:v1 runtime sandbox create --image web:v1 ``` ```ts check import { readFile } from "node:fs/promises"; import { Runtime } from "withruntime"; const runtime = new Runtime(); const image = await runtime.images.build( { name: "web", dockerfile: await readFile("Dockerfile", "utf8"), contextDir: "." }, { onLog: (line) => console.log(line.text) }, ); await using sbx = await runtime.sandboxes.create({ image: "web" }); console.log(image.version, sbx.id); ``` ```python check from withruntime import Runtime runtime = Runtime() with open("Dockerfile") as handle: image = runtime.images.build(name="web", dockerfile=handle.read(), context_dir=".", on_log=lambda line: print(line["text"])) sandbox = runtime.sandboxes.create(image="web") ``` What a Dockerfile can use: - Multi-stage builds. `FROM `, `COPY --from=` and `COPY --from=` all work, and only the stages the image needs are built. `--target` (the SDKs' `target`) builds a named stage instead of the last one. - `ARG` before and after `FROM`, build arguments, and the platform arguments (`TARGETARCH` is `amd64`). - Heredocs: `RUN <.dkr.ecr..amazonaws.com`) | an access key id and secret that may call `ecr:GetAuthorizationToken` and pull; each build asks ECR for a fresh token | | Any other | a user name and a token or password | The same credentials cover `FROM`, `COPY --from=` and a recipe's `base`. In the SDKs: `runtime.images.registries.set(...)`, `.list()` and `.delete(...)`. ## Names, versions and tags Every build of a name gets the next version number. When a build is ready, it takes the tags you gave it, or `latest` when you gave none. A tag points at one version and moves when another version takes it; a later version keeps a tag even if an earlier build finishes after it. Wherever an image is named, including `image` when you create a sandbox, it can be its id, `name` (its `latest` tag), `name:tag` or `name@version`. ```bash no-run runtime image build . -t web:v2 -t web:latest runtime image versions web runtime image tag web@2 prod runtime image untag web@1 prod runtime image rm web@1 ``` Deleting a version removes its tags too. Sandboxes already started from it keep running. ## Start and ready commands An image can say what runs when a sandbox starts from it, and when that sandbox counts as ready. A Dockerfile's `CMD` and `ENTRYPOINT` become the start command and its `HEALTHCHECK` becomes the ready check. You can set or change both with `start`: ```ts check import { Runtime } from "withruntime"; const runtime = new Runtime(); await runtime.images.build({ name: "api", recipe: { pip: ["fastapi", "uvicorn"] }, start: { command: "uvicorn main:app --port 8000", readyPort: 8000, readyTimeoutSeconds: 60 }, }); const sbx = await runtime.sandboxes.create({ image: "api" }); console.log(sbx.info.start); // { state: "ready", readyMs: ... } ``` The start command runs once, in the background, as the sandbox user. The create call answers when the ready check passes: `readyPort` is being listened on, or `readyCommand` exits 0. It answers with `start.state` set to `ready`, `started` (no ready check), `timeout` (the check did not pass within `readyTimeoutSeconds`, 60 by default and at most 300, within the SDKs' own five-minute call deadline), or `exited` (the start command ended first). `start: null` drops what the Dockerfile said. ## Faster rebuilds A build keeps up to three checkpoints of its filesystem: after the base image is pulled, and after the last commands before later steps. A later build of your account that begins with the same steps starts from the latest matching checkpoint instead of from the beginning, so changing your code reruns only the steps after the `COPY` that brings it in. The build log says which image's checkpoint it started from, and `cache` on the image says what it keeps. Checkpoints stay on the server with the image that made them and go when it is deleted. They count toward your image disk quota and are not charged. Pass `cache: false` (`--no-cache`) to build every step from scratch and keep none. An identical build of your account, same plan and same files, is copied at once instead of built. ## Build logs `images.build` streams the log to `onLog` (`on_log`) as it is written. The CLI prints it while it builds, and `runtime image logs --follow` streams it again. Over HTTP, `GET /v1/images/{id}/logs?follow=true` answers with one JSON event per line: each `line`, then `done` with the image. ## Limits | Limit | Value | | -------------------- | ----------------------------------------------------------------------------------------------- | | Builds at once | 1 until your account has bought credit, then 4 | | Build machine | 1 to 8 vCPUs (2 by default), 1 to 16 GiB of memory (4 GiB), 1 to 32 GiB of scratch disk (4 GiB) | | Build time | 60 seconds to 1 hour (30 minutes by default) | | Image size | 512 MiB to 20 GiB (8 GiB by default) | | Build context | 100 MiB compressed, 20,000 files, 2 GiB unpacked | | Context upload | 1 MiB chunks, kept 24 hours after last use; 200 MiB held and 1 GiB uploaded a day per account | | Inline `files` | 256 files and 1 MiB in all | | Dockerfile | 256 KiB, 200 steps across all stages, 16 earlier stages | | Registry credentials | 20 per account | ## Pricing A stored image is charged on its whole file ([pricing](./pricing#snapshots-images-and-volumes)). Building an image is free and does not use trial hours. A free trial keeps its first three images free.