> ## 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 and refresh a reusable custom environment

> Build the supported Dockerfile subset, resolve the immutable UUID, run twice, and refresh after a source change.

`contree build` in CLI 0.9.4 interprets a documented Dockerfile subset on the client and produces Sandboxes image checkpoints. It is distinct from importing an already-built OCI image and is not a general Docker build service.

## Prerequisites and files

Complete [Set up access](/sandboxes/start/set-up-access), install `jq`, and run `uv tool install "contree-cli==0.9.4"`. Create `Dockerfile`:

```dockerfile theme={null}
FROM python:3.12-alpine
ARG MESSAGE=version-1
ENV APP_MESSAGE=${MESSAGE}
WORKDIR /app
COPY verify.py /app/verify.py
RUN python /app/verify.py
```

Create `verify.py` beside it:

```python theme={null}
import os
from pathlib import Path

assert os.environ["APP_MESSAGE"].startswith("version-")
Path("/app/version.txt").write_text(os.environ["APP_MESSAGE"] + "\n")
print(os.environ["APP_MESSAGE"])
```

Run both Bash blocks below in the same shell and the directory containing `Dockerfile` and `verify.py`; the second block reuses `CONTREE_SESSION`, `tag`, and `first_uuid` from the first. Build, resolve the tag to an immutable UUID, and perform two independent read-only runs:

```bash theme={null}
set -eu
export CONTREE_SESSION="docs-custom-env-$$"
tag="docs/custom-env-$CONTREE_SESSION:current"
contree build . --tag "$tag"
contree use "tag:$tag" >/dev/null
first_uuid=$(contree -o json use | jq -er .current_image)
test -n "$first_uuid"
first_read=$(contree run -D -- cat /app/version.txt)
second_read=$(contree run -D -- cat /app/version.txt)
test "$first_read" = version-1
test "$second_read" = version-1
current_uuid=$(contree -o json use | jq -er .current_image)
test "$current_uuid" = "$first_uuid"
printf 'First image: %s\n' "$first_uuid"
printf 'First read: %s\nSecond read: %s\n' "$first_read" "$second_read"
```

Both reads should find `version-1`. They leave the session on the first image because `-D` discards each run's changes. The checks verify that the session still refers to the recorded UUID and print labelled read results.

Rebuild with a new value:

```bash theme={null}
contree build . --build-arg MESSAGE=version-2 --tag "$tag"
contree use "tag:$tag" >/dev/null
second_uuid=$(contree -o json use | jq -er .current_image)
test -n "$second_uuid"
test "$second_uuid" != "$first_uuid"
refreshed_read=$(contree run -D -- cat /app/version.txt)
test "$refreshed_read" = version-2
current_uuid=$(contree -o json use | jq -er .current_image)
test "$current_uuid" = "$second_uuid"
printf 'Refreshed image: %s\n' "$second_uuid"
printf 'Refreshed read: %s\n' "$refreshed_read"
```

The new build resolves to a different UUID. The checks verify that the file contains `version-2` and that the disposable read leaves the session on the refreshed image. A tag can move, so evaluations should use the recorded UUID rather than the example tag.

The labelled reads should show:

```text theme={null}
First read: version-1
Second read: version-1
Refreshed read: version-2
```

The output also includes the two distinct image UUIDs.

The supported CLI subset includes `FROM`, `RUN`, `COPY`/`ADD`, `WORKDIR`, `ENV`, `ARG`, and `USER`; multi-stage `AS` is parsed but not executed. Each `RUN` creates a retained layer, and the client cache can reuse layers. Use `--no-cache` when a refresh must ignore that cache. A failed build should be retried from its declared inputs after correcting the failing directive; do not use the tag for downstream work until the rebuild passes. Checkpoint layers remain subject to image retention.

The verification reads the file created by the Dockerfile `RUN`. CLI 0.9.4 applies Dockerfile `ENV` while interpreting build steps, but this workflow does not rely on that value being inherited by later operations.

Registry imports instead use `images.import_from()` or `images.oci()` and capture a filesystem root. OCI runtime configuration does not define a later Sandboxes command.

The example leaves a named local CLI session, a movable remote tag, and its retained build images. Use a different unique name for another run. Remove local session bookkeeping with the documented CLI session command when it is no longer needed. Removing a tag can make its image eligible for deletion under the [retention policy](/sandboxes/operate/limits-retention-and-usage). Contact [support](/sandboxes/operate/troubleshooting#support-bundle) if you need to delete retained images.

See [Prepare and reuse environments](/sandboxes/guides/prepare-and-reuse-environments) and the [CLI Dockerfile tutorial](/sandboxes/cli/tutorial/build).
