Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Templates

A template is a reusable starting point for launching sandboxes. Build or import it once, then use it to create sandboxes whenever you need the same software and configuration.

Create Your Template

There are two ways to create a template: aenv pull imports an OCI image directly, and aenv build runs Dockerfile instructions inside a temporary build sandbox.

Some defaults below come from your AgentENV config file. This is config/default.toml by default, or the file specified by AENV_CONFIG_PATH.

aenv pull

Pull an existing OCI image as a template and optionally give it a memorable name:

Usage:

aenv pull <image> [options]

Example:

aenv pull ubuntu:24.04
aenv pull ubuntu:24.04 --name my-base

--name is optional. Without it, AgentENV uses the image repository name. A template name can be used anywhere a template ID is accepted.

Argument or optionDefaultDescription
<image>RequiredOCI image reference. Short names such as ubuntu:24.04 and full references are supported.
--name <name>Image repository nameAssign a human-readable template name.
--cpu <count>[machine].vcpu_count from your config fileSet the template’s vCPU count. Alias: --cpu-count.
--memory <MiB>[machine].mem_size_mib from your config fileSet the template’s memory. Aliases: --memory-mb, --mem.
--start-cmd <cmd>NoneRun a command before capturing the template snapshot.
--ready-cmd <cmd>sleep 20 when --start-cmd is set; otherwise nonePoll a shell command every two seconds until it exits successfully.
--probe <port>NoneWait for TCP on localhost:<port>. Cannot be combined with --ready-cmd.
-d, --detachOffSubmit the build and return immediately instead of waiting.
--timeout <seconds>No timeoutLimit how long the CLI waits for the build. Cannot be combined with --detach.

Env, WorkingDir, and User are automatically inherited from the OCI image config. See Runtime Configuration for the full field list.

aenv build

Build a Dockerfile with BuildKit inside a temporary microVM, then convert the result to OverlayBD and capture a template. The CLI and full AgentENV installers include a private aenv-buildctl client. Source builds can use --buildctl to select a compatible client. Docker and a staging registry are not required on the CLI machine. On Linux and macOS, the local buildctl connection uses a Unix socket in a directory accessible only to the current user.

aenv build <context> --name <name> [options]
# From the repository root:
aenv build . -f deploy/docker/Dockerfile.agentenv --name aenv
Argument or optionDefaultDescription
<context>RequiredLocal context directory, as with docker build.
-f, --file <path><context>/DockerfileDockerfile path; explicit relative paths resolve from the current directory.
--name <name>RequiredAssign the template name.
--cpu <count>[machine].vcpu_count from your config fileSet the template’s vCPU count. Alias: --cpu-count.
--memory <MiB>[machine].mem_size_mib from your config fileSet the template’s memory. Aliases: --memory-mb, --mem.
--start-cmd <command>Image ENTRYPOINT/CMDOverride template startup; an empty string disables startup.
--ready-cmd <command>Image HEALTHCHECK, or the normal startup delayOverride the command that must succeed before snapshot capture.
--build-arg KEY=VALUENoneBuild argument; repeatable.
--secret <spec>NoneNative BuildKit secret mounts; repeatable.
--no-cacheFalseRebuild without cached instructions. BuildKit also resets cache mounts used by those instructions.
--buildctl <path>aenv-buildctl beside aenvLocal client executable.
--progress <format>autoThree-stage bar on terminals, plain logs when redirected. plain selects plain logs; tty selects BuildKit’s native display.
--timeout <seconds>3600Builder preparation and Dockerfile build deadline; the CLI allows 10 additional minutes for publication.

COPY and ADD resolve from the context directory, independently of the Dockerfile’s location. Local directories are supported; URL and stdin contexts are not supported. The final Dockerfile stage is always published. Select base images with FROM (or ARG used by FROM) and startup with ENTRYPOINT/CMD. --start-cmd and --ready-cmd override startup and readiness independently. There are no image, stage-selection, SSH, or builder-resource overrides in the build CLI.

Managed builder settings belong to the server configuration:

[template_build]
max_concurrent_builds = 4
builder_image = "docker.io/moby/buildkit:v0.33.0"
builder_cpu_count = 16
builder_memory_mb = 32768
cache_size_mb = 65536

Each node admits at most max_concurrent_builds managed builds, including preparation, image publication, and cleanup. Excess builder PUT requests return HTTP 429 without changing the waiting build; retry once capacity is available. Each build permits up to 8 simultaneous WebSocket tunnels, including pending connections and upgrades. Excess tunnel connections also return HTTP 429.

The 64 GiB disk is the persistent /var/lib/buildkit data volume, where image layers, build contexts, and cache mounts live. Its capacity applies when creating the cache; changing the setting does not resize an existing volume. These resources are separate from the resulting template’s CPU and memory. Dockerfile build requests reject a cache capacity above volume.max_size_mb; a smaller volume limit does not prevent the server from starting or serving other APIs.

BuildKit handles multi-stage builds, COPY, ADD, .dockerignore, cache mounts, and Dockerfile syntax. The CLI remains connected during the build, streams build progress, and exits nonzero on failure. The server provisions and releases an internal worker for each template build. The first Dockerfile build prepares a reusable builder snapshot in a private namespace of the configured snapshot repository. Nodes sharing that repository restore the same builder and attach a new cache volume before starting BuildKit. Concurrent first requests on a node share initialization. Simultaneous first builds on different nodes may both prepare a builder; subsequent builds reuse the published snapshot. Builder image, CPU, memory, virtualization mode, and readiness setup identify the reusable snapshot; changing those inputs prepares a new builder. The CLI uses only template and build IDs; workers are absent from public sandbox listings and endpoints. Cancellation and deadlines release the worker and discard its incomplete cache child. A hard client failure is covered by the build deadline, and server restart recovers unfinished builds and cache reservations from a durable journal. Failed cleanup is retained and retried every 30 seconds while the server runs, and cancellation retries use the same cleanup path. Active builds reject template deletion until cancellation or completion. An unreadable entry does not block recovery of other builds or prevent server startup. Once BuildKit succeeds, publication proceeds even if the CLI is interrupted; its status remains available:

aenv template watch my-template

aenv template watch has no timeout option. Stop the local watch with Ctrl-C; the remote build continues.

The node reads the completed image directly from the builder by SHA-256 digest. It verifies the transferred bytes and converts only missing layers, reusing the same content-addressed OverlayBD cache as registry image imports. The completed image never travels through the CLI. Only the final image configuration becomes template configuration; intermediate stages and build-time arguments are not template environment variables.

Publishing also boots the image and runs its startup command. An image can compile and convert successfully but fail this step if its entrypoint needs devices absent from the guest. For example, deploy/docker/Dockerfile.agentenv starts the host AgentENV server, which requires /dev/kvm; it cannot run as a template in a guest without nested KVM support. Use --start-cmd to select another startup command, or --start-cmd "" --ready-cmd true to capture without starting the image’s application or running its health check.

The guest image needs /bin/sh for envd process execution, including exec-form Dockerfile commands. Numeric USER values, including UIDs without an account and explicit UID/GID pairs, are preserved during startup and restore.

Caches are shared across template names and nodes using the configured snapshot repository. Each build clones the latest immutable cache seed into its own writable volume. Sequential builds inherit the preceding build’s accumulated instruction cache and RUN --mount=type=cache data. Concurrent builds can fork the same seed without waiting for each other; the last successfully published cache becomes the next seed. Sibling cache additions are not merged.

After image import, the node stops BuildKit, checkpoints and publishes its cache volume through the normal volume freeze and capture path, and stops the VM without saving its memory, rootfs, or device state. A failed shutdown or cache capture keeps the previous shared seed. Volume ownership remains held until the VM stops.

Cache volumes use normal volume publication, uploading only missing layers. Cache publication adds cleanup time, but a failed cache upload does not fail an otherwise successful template build or replace the previous seed. Old cache volumes are removed after active children release their leases. The shared cache record commits the new seed and pending retirements together, and cleanup retries retirements independently of individual builds. BuildKit’s garbage collector manages cache contents. Cache sharing uses the repository’s existing API-key trust boundary. Registry credentials come from the local BuildKit session and normal Docker credential configuration.

The BuildKit API extends the existing template/build lifecycle:

  1. POST /v3/templates allocates template/build IDs using the existing request.
  2. PUT /templates/{templateID}/builds/{buildID}/builder prepares the worker. It accepts optional startCmd, readyCmd, and timeout, and returns imageName. A build that has already started returns 409.
  3. Poll GET /templates/{templateID}/builds/{buildID}/status until building; waiting means the worker is still preparing.
  4. GET /templates/{templateID}/builds/{buildID}/builder opens the authenticated binary WebSocket for BuildKit. Use the returned name in --output type=image,name=<imageName>,oci-mediatypes=true.
  5. The server observes successful BuildKit completion, verifies the image digest, and automatically imports and publishes the template. No client submission request is needed. Continue polling status until ready or error.
  6. DELETE /templates/{templateID}/builds/{buildID}/builder cancels the build before publication. Once publication starts, cancellation returns 409 and the server finishes publication and releases the worker.

These endpoints require the API key. The gateway schedules worker preparation and binds subsequent requests to that node. Status polling falls back to the shared repository after the binding expires. The unique image name identifies this build among cached BuildKit history; a dropped connection is never treated as successful completion. Failed solves become error. Existing statuses are unchanged; building includes image import and publication.

Image import has a one-hour deadline; metadata is limited to 4 MiB per blob and images to 1024 layers and 64 GiB of compressed data. Worker connections can drain for up to ten seconds after image import before the worker is stopped. Existing declarative template endpoints retain their behavior.

By default, image ENTRYPOINT and CMD are combined for startup through /bin/sh -c. Dockerfile HEALTHCHECK supplies the readiness command before snapshot capture; shell checks honor Dockerfile SHELL, and HEALTHCHECK NONE disables the check. It uses AgentENV’s readiness polling and deadline, not Docker’s health-monitoring intervals or restart behavior. Without a check, startup uses the normal template readiness delay. --start-cmd and --ready-cmd (API fields startCmd and readyCmd) override these commands independently without modifying the image configuration. Images must satisfy AgentENV’s normal guest runtime requirements; EXPOSE and VOLUME are metadata, not Docker runtime services.

API and repository compatibility

Existing template creation, declarative build, status, listing, and deletion endpoints remain available. BuildKit adds PUT, GET, and DELETE on the builder resource beneath an existing build. It adds no competing template creation endpoint or image submission endpoint. Upgrade the gateway and all nodes serving build requests before using BuildKit.

Existing remote snapshots and volumes need no migration. Their catalog keys, artifact paths, and layer formats are unchanged. Builder snapshots use the separate template-build/builder namespace; the new cache head and retirement record uses template-build/cache-head.json. Cache volumes retain the normal volume format.

Startup metadata adds an optional shell field. Older records omit it, retain /bin/bash -lc behavior, and are written back without adding the field. New BuildKit templates use /bin/sh -c. Older servers can deserialize these records, but ignore the shell field and drop it when rewriting the record. Building a derived template on an older server therefore uses Bash and can fail for images without Bash or change shell behavior. Keep builds derived from BuildKit templates on upgraded nodes; rolling back does not provide full BuildKit template support.

Runtime Configuration

Both methods read the same set of OCI image config fields. For aenv pull, these come from the image config or flags; for aenv build, they are set by the corresponding Dockerfile instructions executed during the build. The following fields from the OCI image-spec config object are recognised:

OCI fieldDockerfile instructionRuntime effect
EnvENVEnvironment variables injected into every sandbox process
WorkingDirWORKDIRDefault working directory
UserUSERDefault user
Entrypoint / CmdENTRYPOINT / CMDMapped to startCmd for aenv build; use --start-cmd explicitly for aenv pull
ExposedPortsEXPOSEStored as metadata only
VolumesVOLUMEStored as metadata only
LabelsLABELStored as metadata only

Manage Templates

List templates

aenv template list        # alias: aenv template ls
aenv template list --output json

Displays all templates with their ID, name, build status, CPU, memory, disk size, and last-updated timestamp. --output accepts table or json. It defaults to table in an interactive terminal and json when output is piped or redirected.

List template builds

List the complete build history for one template:

curl -H 'X-API-Key: test-key' \
  http://127.0.0.1:8000/templates/<template-id>

Check a template alias

Check whether an alias exists and resolve it to a template ID:

curl -H 'X-API-Key: test-key' \
  http://127.0.0.1:8000/templates/aliases/<alias>

Delete a template

aenv template delete <template-id-or-name>   # alias: aenv template rm

Relationship to Snapshots

Templates are the API and UX layer. Snapshots are the durable runtime layer.

  • A template build publishes one committed snapshot.
  • A template ID or alias resolves to one committed snapshot.
  • A sandbox created from a template resumes from that snapshot.

If you want the storage and runtime model underneath templates, see Snapshots.