> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tokenfactory.nebius.com/llms.txt
> Use this file to discover all available pages before exploring further.

# build - Build an image from a Dockerfile

Build an image from a `Dockerfile`. Each directive runs against the
contree API and produces a new image layer; successful layers are
materialised as branches named `layer:<chain-hash>` so re-running the
same Dockerfile reuses prior work.

## Synopsis

```bash theme={null}
contree build [CONTEXT] [--dockerfile PATH] [--tag NAME[:TAG]]
              [--build-arg K=V ...] [--no-cache] [--timeout SEC]
```

* `CONTEXT` – build context directory (default `.`).
* `--dockerfile PATH` – override the default `<CONTEXT>/Dockerfile`.
* `--tag NAME[:TAG]` – tag the final image via `PATCH /v1/images/{uuid}/tag`.
* `--build-arg KEY=VALUE` – supply a value for an `ARG` declared in the
  Dockerfile (repeatable).
* `--no-cache` – ignore existing `layer:<hash>` branches and rebuild.
* `--timeout SEC` – per-`RUN` operation timeout in seconds (default 600).

## Help output

<div className="contree-terminal" style={{"border":"1px solid rgba(255,255,255,0.15)","borderRadius":8,"overflow":"hidden","margin":"1rem 0"}}><div style={{"background":"#292929","height":24,"display":"flex","alignItems":"center","padding":"0 12px","position":"relative"}}><div style={{"display":"flex","alignItems":"center","flexShrink":0}}><span style={{"display":"inline-block","width":10,"height":10,"borderRadius":"50%","background":"#ff5f57","marginRight":6}} /><span style={{"display":"inline-block","width":10,"height":10,"borderRadius":"50%","background":"#febc2e","marginRight":6}} /><span style={{"display":"inline-block","width":10,"height":10,"borderRadius":"50%","background":"#28c840"}} /></div><div style={{"position":"absolute","left":0,"right":0,"textAlign":"center","fontFamily":"-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif","fontWeight":"bold","color":"#999"}}>\$ contree build --help</div></div><pre style={{"margin":0,"borderRadius":0,"padding":"8px 10px","background":"#1e1e1e","overflowX":"auto"}}><code style={{"fontFamily":"JetBrains Mono, SF Mono, SFMono-Regular, Menlo, Monaco, Cascadia Mono, Segoe UI Mono, Roboto Mono, Oxygen Mono, Ubuntu Monospace, Source Code Pro, Fira Mono, Droid Sans Mono, Consolas, Courier New, monospace","color":"#c5c8c6","whiteSpace":"pre-wrap"}}><span style={{"color":"#61afef","fontWeight":"bold"}}>usage: </span><span style={{"color":"#c678dd","fontWeight":"bold"}}>contree build</span><span style={{"color":"#c5c8c6"}}> \[</span><span style={{"color":"#98c379"}}>-h</span><span style={{"color":"#c5c8c6"}}>] \[</span><span style={{"color":"#56b6c2"}}>--dockerfile </span><span style={{"color":"#e5c07b"}}>PATH</span><span style={{"color":"#c5c8c6"}}>] \[</span><span style={{"color":"#56b6c2"}}>--tag </span><span style={{"color":"#e5c07b"}}>NAME\[:TAG]</span><span style={{"color":"#c5c8c6"}}>] \[</span><span style={{"color":"#56b6c2"}}>--build-arg </span><span style={{"color":"#e5c07b"}}>KEY=VALUE</span><span style={{"color":"#c5c8c6"}}>]</span><br /><span style={{"color":"#c5c8c6"}}>                     \[</span><span style={{"color":"#56b6c2"}}>--no-cache</span><span style={{"color":"#c5c8c6"}}>] \[</span><span style={{"color":"#98c379"}}>-t </span><span style={{"color":"#e5c07b"}}>TIMEOUT</span><span style={{"color":"#c5c8c6"}}>]</span><br /><span style={{"color":"#c5c8c6"}}>                     </span><span style={{"color":"#98c379"}}>\[context]</span><br /><br /><span style={{"color":"#c5c8c6"}}>Build an image from a Dockerfile.</span><br /><br /><span style={{"color":"#c5c8c6"}}>Reads the Dockerfile at the given path (default \`\`\<CONTEXT>/Dockerfile\`\`)</span><br /><span style={{"color":"#c5c8c6"}}>and applies each directive against an isolated build session keyed by</span><br /><span style={{"color":"#c5c8c6"}}>the absolute path of the context directory. Successful layers are</span><br /><span style={{"color":"#c5c8c6"}}>materialised as branches named \`\`layer:\<chain-hash>\`\` so that</span><br /><span style={{"color":"#c5c8c6"}}>re-running the same Dockerfile reuses prior work.</span><br /><br /><span style={{"color":"#c5c8c6"}}>Supported directives (MVP): FROM, RUN, COPY, ADD (local files/dirs</span><br /><span style={{"color":"#c5c8c6"}}>and http(s) URLs; no tar auto-extraction), WORKDIR, ENV, ARG, USER.</span><br /><span style={{"color":"#c5c8c6"}}>Other Dockerfile directives parse cleanly but are skipped with a</span><br /><span style={{"color":"#c5c8c6"}}>warning (CMD, ENTRYPOINT, LABEL, EXPOSE, VOLUME, STOPSIGNAL,</span><br /><span style={{"color":"#c5c8c6"}}>MAINTAINER, HEALTHCHECK, ONBUILD, SHELL).</span><br /><br /><span style={{"color":"#61afef","fontWeight":"bold"}}>positional arguments:</span><br /><span style={{"color":"#c5c8c6"}}>  </span><span style={{"color":"#98c379","fontWeight":"bold"}}>context</span><span style={{"color":"#c5c8c6"}}>               Build context directory (default: .)</span><br /><br /><span style={{"color":"#61afef","fontWeight":"bold"}}>options:</span><br /><span style={{"color":"#c5c8c6"}}>  </span><span style={{"color":"#98c379","fontWeight":"bold"}}>-h</span><span style={{"color":"#c5c8c6"}}>, </span><span style={{"color":"#56b6c2","fontWeight":"bold"}}>--help</span><span style={{"color":"#c5c8c6"}}>            show this help message and exit</span><br /><span style={{"color":"#c5c8c6"}}>  </span><span style={{"color":"#56b6c2","fontWeight":"bold"}}>--dockerfile</span><span style={{"color":"#c5c8c6"}}> </span><span style={{"color":"#e5c07b","fontWeight":"bold"}}>PATH</span><span style={{"color":"#c5c8c6"}}>     Dockerfile path (default: \<context>/Dockerfile) (default: )</span><br /><span style={{"color":"#c5c8c6"}}>  </span><span style={{"color":"#56b6c2","fontWeight":"bold"}}>--tag</span><span style={{"color":"#c5c8c6"}}> </span><span style={{"color":"#e5c07b","fontWeight":"bold"}}>NAME\[:TAG]</span><span style={{"color":"#c5c8c6"}}>      Tag the final image (default: )</span><br /><span style={{"color":"#c5c8c6"}}>  </span><span style={{"color":"#56b6c2","fontWeight":"bold"}}>--build-arg</span><span style={{"color":"#c5c8c6"}}> </span><span style={{"color":"#e5c07b","fontWeight":"bold"}}>KEY=VALUE</span><br /><span style={{"color":"#c5c8c6"}}>                        Build-time variable (repeatable)</span><br /><span style={{"color":"#c5c8c6"}}>  </span><span style={{"color":"#56b6c2","fontWeight":"bold"}}>--no-cache</span><span style={{"color":"#c5c8c6"}}>            Ignore cached layers and rebuild</span><br /><span style={{"color":"#c5c8c6"}}>  </span><span style={{"color":"#98c379","fontWeight":"bold"}}>-t</span><span style={{"color":"#c5c8c6"}}>, </span><span style={{"color":"#56b6c2","fontWeight":"bold"}}>--timeout</span><span style={{"color":"#c5c8c6"}}> </span><span style={{"color":"#e5c07b","fontWeight":"bold"}}>TIMEOUT</span><br /><span style={{"color":"#c5c8c6"}}>                        Timeout in seconds for each RUN step (default: 600)</span><br /><br /><span style={{"color":"#c5c8c6"}}>examples:</span><br /><span style={{"color":"#c5c8c6"}}>  contree build .</span><br /><span style={{"color":"#c5c8c6"}}>  contree build . --tag myimage:latest</span><br /><span style={{"color":"#c5c8c6"}}>  contree build --dockerfile ./Dockerfile.test ./app</span><br /><span style={{"color":"#c5c8c6"}}>  contree build --build-arg VERSION=1.2 .</span><br /><span style={{"color":"#c5c8c6"}}>  contree build --no-cache .</span><br /><br /><span style={{"color":"#c5c8c6"}}>for coding agents:</span><br /><span style={{"color":"#c5c8c6"}}>  mutating command, may create operations against the API</span><br /><span style={{"color":"#c5c8c6"}}>  layer cache is per-context (session keyed by abspath(context))</span><br /><span style={{"color":"#c5c8c6"}}>  use --no-cache to bypass cached layers and rebuild from scratch</span><br /><br /><span style={{"color":"#c5c8c6"}}>agent note:</span><br /><span style={{"color":"#c5c8c6"}}>  Before using this command in an automated workflow, read:</span><br /><span style={{"color":"#c5c8c6"}}>    contree agent</span></code></pre></div>

## Examples

```bash theme={null}
# Simplest build; finds ./Dockerfile, tags the result
contree build . --tag myapp:dev

# Out-of-tree Dockerfile
contree build ./service --dockerfile ./service/Dockerfile.prod --tag svc:prod

# Override build-time variables
contree build . --build-arg VERSION=2.5 --build-arg DEBUG=1

# Force a rebuild ignoring cached layers
contree build . --no-cache --tag myapp:dev
```

## Supported directives (MVP)

| Directive                                                                                                       | Behaviour                                                                                                                                                                                                                                                                                                                                 |
| --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `FROM ref[:tag] [AS name]`                                                                                      | Resolves the base image. If the tag is not found locally, the build auto-imports it via `POST /v1/images/import`. `AS name` is parsed but ignored (multi-stage is Phase 2).                                                                                                                                                               |
| `RUN ...`                                                                                                       | Shell-form (`RUN echo hi`) or JSON exec-form (`RUN ["echo","hi"]`). Spawns `POST /v1/instances`, polls until terminal status, captures the resulting image.                                                                                                                                                                               |
| `COPY [--chown=...] [--chmod=...] SRC... DEST`                                                                  | Walks local sources relative to the build context, applies `.dockerignore`, uploads files (with SHA256 dedup), and stages them for the next `RUN`.                                                                                                                                                                                        |
| `ADD ...`                                                                                                       | Local paths behave like `COPY`. `https://`/`http://` URLs are **streamed straight from the source socket into `POST /v1/files`** (no temp file on disk); the URL plus its `ETag`/`Last-Modified`/`Content-MD5` validators are cached so repeat builds skip the download via a conditional `HEAD`. Tar auto-extraction is not implemented. |
| `WORKDIR /path`                                                                                                 | Sets the working directory for subsequent directives.                                                                                                                                                                                                                                                                                     |
| `ENV KEY=VALUE ...`                                                                                             | Accumulates environment variables passed to every `RUN`.                                                                                                                                                                                                                                                                                  |
| `ARG NAME[=DEFAULT]`                                                                                            | Declares a build-time variable. Overridden by `--build-arg`.                                                                                                                                                                                                                                                                              |
| `USER name`                                                                                                     | Subsequent `RUN` commands are wrapped in `su -s /bin/sh -c '<cmd>' <name>`.                                                                                                                                                                                                                                                               |
| `CMD`, `ENTRYPOINT`, `LABEL`, `EXPOSE`, `VOLUME`, `STOPSIGNAL`, `MAINTAINER`, `HEALTHCHECK`, `ONBUILD`, `SHELL` | Parsed but skipped with a warning.                                                                                                                                                                                                                                                                                                        |

`COPY --from=stage` is a Phase 2 feature; in MVP it warns and skips.

## Sessions and layer cache

Builds run in a dedicated session keyed by the absolute path of the
context directory: `build:<sha16(abspath(context))>`. Re-running the
same Dockerfile in the same context reuses cached layers across
invocations of `contree build`; switching to `--no-cache` rebuilds
everything.

Layers are stored as branches whose names are the chain-hash of:

```
sha256(parent_layer_hash || state(workdir/env/user/args) || directive || pending_files)
```

To inspect the resulting branches:

```bash theme={null}
contree session list --filter build:
contree session show
```

<Note>
  `build` is **project-scoped from the user’s point of view**: it does
  not bind to the agent’s `-S <key>` session. Passing `-S` is harmless
  but does not move that key’s image. After a successful build, attach
  the result to your normal agent session by tag:

  ```bash theme={null}
  contree build . --tag myapp:dev
  contree -S agent_verify use tag:myapp:dev
  contree -S agent_verify run -D -- myapp --version
  ```
</Note>

## `.dockerignore`

`contree build` reads `<CONTEXT>/.dockerignore` and filters every
`COPY`/`ADD` walk. Rules are matched in order against POSIX-style
paths relative to the context root; the last matching rule wins,
so `!` re-includes a previously ignored path.

```
# .dockerignore
**/*.log
.env*
node_modules
!logs/keep.log
```

Globs:

* `*` matches a single path segment (does not cross `/`).
* `**` matches zero or more path components.
* `?` matches one character.
* `[abc]` is a character class.
* Trailing `/` matches a directory and everything below it.

The default exclude list from `run --file` (`.git`, `*.pyc`,
`__pycache__`, `.venv`, `node_modules`, `dist`, `build`, etc.) is
always applied on top of `.dockerignore`.

## Variable substitution

`$VAR` and `${VAR}` are expanded in `FROM`, `RUN`, `COPY`/`ADD`
arguments, `WORKDIR`, `ENV` values, and `USER`. The value source is:

1. `--build-arg KEY=VALUE` (highest priority for declared `ARG` names).
2. `ENV` directives processed so far.
3. `ARG` defaults.
4. Empty string for unknown names.

## End-to-end demo

A small example lives in `docs/examples/build-demo/`. The Dockerfile
exercises `FROM`, `ARG`, `ENV`, `WORKDIR`, two `COPY` directives (file
and directory), and two `RUN` directives. A `.dockerignore` filters
log files and `__pycache__` from the upload.

```dockerfile theme={null}
% docs/examples/build-demo/Dockerfile
FROM python:3.12-alpine

ARG GREETING=hello
ENV APP_GREETING=${GREETING}

WORKDIR /app

COPY hello.py /app/hello.py
COPY src /app/src
ADD https://github.com/nebius/contree-cli/archive/refs/heads/master.zip /tmp/contree-cli.zip

RUN python -c "import sys; print('python', sys.version)"
RUN python -m zipfile -e /tmp/contree-cli.zip /opt/
RUN pip install --no-cache-dir /opt/contree-cli-master
RUN contree --help | head -20
RUN python /app/hello.py
```

The `ADD` line streams the zip straight from GitHub into the contree
API (no local temp file). The subsequent `RUN` steps unpack it,
`pip install` the project, and prove the installed `contree` binary
works inside the built image.

```dockerfile theme={null}
% docs/examples/build-demo/.dockerignore
**/*.log
**/__pycache__
.env*
```

Build and tag it:

```bash theme={null}
contree build docs/examples/build-demo --tag contree-cli-build-demo:latest
```

Expected output (truncated):

```text theme={null}
[INFO] RUN spawned op=019e... RUN python -c "import sys; print('python', sys.version)"
[INFO] stdout:
python 3.12.13 ...

[INFO] RUN spawned op=019e... RUN python /app/hello.py
[INFO] stdout:
+---------------+
|     hello     |
| contree build |
+---------------+

[INFO] tagged <uuid> as contree-cli-build-demo:latest
IMAGE                                 TAG                            SESSION
<uuid>                                contree-cli-build-demo:latest  build:<sha16>
```

Re-running the same command without `--no-cache` produces layer cache
hits, and the `ADD URL` step short-circuits at the `HEAD` probe (look
for `URL cache hit (HEAD validators match)` in the log) – no body
download, no upload.

## See also

* [run - Execute a command in the sandbox](./run) – the single-shot version of what `RUN` does.
* [session - Manage sessions, branches, history](./session) – inspect or branch the layer history.
* [images - List and import images](./images) – list, import, and tag images directly.
