Handbook
Requirement templates and container type catalog
Fleet stores operator-defined container types in etc/containers/types.json under --data-dir. Optional **requirements[] on each type references stable slugs declared in **etc/containers/requirement_templates.json.
Updated
Built or pulled template images are recorded in **etc/containers/build_cache.json (alongside SQLite). Template builds are a convenience for trusted operator hosts; they are not** a substitute for sandboxing untrusted workloads (see 01-workspace-upload.md).
API request -> resolve fingerprints -> cache hit path
cache miss -> docker build or pull -> updated cache -> job argv injectFiles
| Path | Purpose |
|---|---|
etc/containers/types.json |
Categories + types (id, category_id, container_class, title, optional requirements, optional capability overrides). |
etc/containers/requirement_templates.json |
Version + templates[]: each row id, title, kind (dockerfile | image), ref, optional notes, optional image_semver (for kind: image only — policy/compare string; ref remains the pull target). |
etc/containers/build_cache.json |
Cache entries keyed by requirement bundle fingerprint → resolved image tag, timestamps, errors. |
etc/containers/dockerfiles/ |
Recommended location for Dockerfile paths referenced as ref (relative to etc/containers/). |
Requirement bundles vs builds
Cache fingerprint: GET /v1/container-templates/resolve?requirements=a,b and POST /v1/container-templates/build with multiple requirement_ids both participate in one bundle fingerprint for the sorted set of requirement template rows (see bundle_fingerprint in fleet_server/container_templates.py).
What Fleet builds or pulls: run_template_build (same module) applies these rules:
- Single requirement id:
kind: dockerfilerunsdocker buildonce (build context = the Dockerfile’s parent directory).kind: imagerunsdocker pullfor that template’sref. - Multiple requirement ids: Supported only when every template is
kind: imageand all share the sameref(effectively onedocker pull). Otherwise Fleet fails withmulti_requirement_build_supported_only_for_single_dockerfile_or_all_same_image— search Fleet logs for that token when debugging failed resolves or builds.
What Fleet does not do: Fleet does not merge several kind: dockerfile templates into one runnable image. Encode the full dependency stack (“pre-installed software”) in one Dockerfile, register it under one requirement template id, or publish one registry image and reference it with kind: image.
Environment variables
| Variable | Effect |
|---|---|
FLEET_TEMPLATE_PACKAGE_UPLOAD_MAX_BYTES |
Default 64 MiB — max HTTP body for PUT /v1/container-templates/{id}/package. |
FLEET_TEMPLATE_PACKAGE_MAX_UNCOMPRESSED_BYTES |
Extracted bytes limit for template packages (default 120 MiB). |
FLEET_TEMPLATE_PACKAGE_MAX_FILES |
Max regular files in a template archive (default 5000). |
FLEET_TEMPLATE_PACKAGE_MAX_PATH_DEPTH |
Max path depth (default 40). |
FLEET_TEMPLATE_BUILD_NETWORK |
Opt-out. By default Fleet allows Docker’s normal network so docker build can pull bases and kind: image templates run docker pull. Set to 0, false, or no to use docker build --network none and to block docker pull for pinned images. |
FLEET_PREFETCH_TEMPLATE_IMAGES |
Opt-out. By default Fleet starts a background prefetch that runs docker build / docker pull once per declared template ID at process start (errors are logged; the server keeps running). Set to 0, false, or no to skip—useful when the catalog is huge and startup latency matters. |
FLEET_DOCKER_BIN |
Override path to the docker CLI. |
FLEET_DOCKER_BUILDKIT |
1 / true — always set DOCKER_BUILDKIT=1 for docker build (fails if buildx is missing). 0 / false — always use the legacy builder (DOCKER_BUILDKIT=0). Unset (default) — try BuildKit first; if Docker reports a missing or broken buildx error, Fleet retries once with BuildKit off so hosts with Engine-only installs still build. For best performance on modern installs, add the docker-buildx-plugin package (see Docker docs) and keep this unset. |
HTTP API (bearer auth when FLEET_BEARER_TOKEN is set)
| Method | Path | Purpose |
|---|---|---|
| GET | /v1/container-types |
Read catalog (unchanged shape: categories, types, types_materialized). |
| PUT | /v1/container-types |
Replace full validated catalog document. |
| POST | /v1/container-types |
Append one type row (id must be new). |
| PUT | /v1/container-types/{id} |
Update one type row. |
| DELETE | /v1/container-types/{id} |
Remove type (empty is reserved; blocked if services reference type_id or running jobs use container_class). |
| PUT | /v1/container-templates/{requirement_id}/package |
Raw body: gzip (or plain) tar archive. Extracts to etc/containers/dockerfiles/{id}/ and upserts a kind: dockerfile row. Query: optional title, notes, replace (0 / false / no = refuse if the template and Dockerfile already exist). Optional header X-Template-Package-Sha256 (hex) must match the body. |
| GET | /v1/container-templates |
Read requirement templates document + paths. |
| PUT | /v1/container-templates |
Replace full requirement templates document (validated). |
| GET | /v1/container-templates/status |
Build cache JSON + in-process build state. |
| GET | /v1/container-templates/resolve?requirements=a,b |
Resolve cached image for the requirement set; on cache miss builds/pulls by default. Pass build_if_missing=0 (or false / no) only when you must skip Docker and accept not_in_cache. |
| POST | /v1/container-templates/build |
Body {"requirement_ids":["slug",…]} — run docker build or docker pull per rules in fleet_server/container_templates.py. |
| POST | /v1/jobs |
Optional: meta.use_fleet_template_image, meta.requirements, optional meta.build_template_if_missing (defaults on unless explicitly false, 0, or the strings false / no)—resolves image and rewrites the Docker run image token in argv (supports docker … run and docker container run, and paths whose basename is docker). |
Admin UI
Under Container types, operators get a table with Edit / Delete / Add type. Under Requirement templates, rows can be edited locally then Save templates persists requirement_templates.json. Build requirement bundle calls POST /v1/container-templates/build.
Argv injection limitations
When POST /v1/jobs uses meta.use_fleet_template_image, Fleet rewrites the first image-looking token after docker … run (or docker container run) by skipping short and long options. Shapes where the image immediately follows a short flag without a separate value (for example docker run --rm myimage …) can be parsed incorrectly because --rm may consume the next token as its value.
Recommended argv for reliable injection: place at least one -e NAME=value (or another flag that takes a separate argument) between run and the image, or put the image as the first non-option token after all flags with explicit values, e.g. docker run -e FLEET_PLACEHOLDER=1 myimage:tag cmd ….
Certificator (or other clients): discover, compare semver, upsert
GET /v1/container-types— see whichid/container_class/requirements[]already exist.GET /v1/container-templates— read each template’srefand optionalimage_semver(forkind: image).- Compare
image_semverandrefto your policy; if stale or missing rows, build a full document andPUT /v1/container-templates. - Optionally
POST /v1/container-templates/buildthenGET /v1/container-templates/resolve?requirements=…before enqueueing jobs.
Changing image_semver or ref for an image template changes the bundle fingerprint (cache key), so Fleet treats it as a new resolved bundle after rebuild.
Remote API E2E (optional)
From a dev machine with Docker + network access to a real Fleet host, you can run tests/test_remote_fleet_container_templates_e2e.py: it GETs health, PUTs a disposable kind: image template (default alpine:3.20), POSTs /v1/container-templates/build, GETs resolve, POSTs a docker_argv job with meta.use_fleet_template_image, polls GET /v1/jobs/{id}, then restores templates by PUT without the disposable row.
export RUN_REMOTE_FLEET_CONTAINER_API_E2E=1
export FORGE_FLEET_BASE_URL=https://your-fleet.example
export FORGE_FLEET_BEARER_TOKEN=...
cd forge-fleet && PYTHONPATH=. python3 -m pytest tests/test_remote_fleet_container_templates_e2e.py -v
Unset RUN_REMOTE_FLEET_CONTAINER_API_E2E (or set SKIP_REMOTE_FLEET_CONTAINER_API_E2E=1) so CI does not hit production. Optional: FLEET_REMOTE_E2E_IMAGE to override the pull image.
Archive layout: The extracted tree must contain a Dockerfile at the root of the archive, or under one top-level directory (e.g. my_ctx/Dockerfile). Put PyPI / system package install steps in that Dockerfile. No certificator template ships inside the forge-fleet git tree; publish the reference package from forge-certificators (fleet-container-template/, scripts/fleet/package-certificator-template.sh, or compat scripts/package-fleet-certificator-template.sh).
Troubleshooting: certificator certificator_source_ingest_worker template
Forge Fleet does not seed a default Dockerfile. On a fresh data directory, GET /v1/container-templates may list no dockerfile rows until you install one.
- In forge-certificators, run
./scripts/fleet/package-certificator-template.sh(or./scripts/package-fleet-certificator-template.sh) to producecertificator_source_ingest_worker.tar.gz. - Upload:
PUT /v1/container-templates/certificator_source_ingest_worker/packagewith the archive as the raw body and admin bearer auth (optional querytitle,notes,replace=1). - Build:
POST /v1/container-templates/buildwith{"requirement_ids":["certificator_source_ingest_worker"]}(needs Docker on the Fleet host and template build network unless opted out).
If docker build fails, inspect GET /v1/container-templates/status and Fleet logs. If the Dockerfile references outdated git+https://github.com/.../forge-certificators, replace it with the package from forge-certificators and re-upload.
Troubleshooting (historical): stale on-disk Dockerfile from git-based pip
Older deployments may still have etc/containers/dockerfiles/certificator_source_ingest_worker/Dockerfile from manual edits. Replace it by uploading a fresh package (PUT …/package) or editing files under paths.dockerfiles_root then POST /v1/container-templates/build.
Manual QA (admin)
- Open
/admin/with a browser; set bearer in localStorage if your Fleet usesFLEET_BEARER_TOKEN. - Container types: Add a type, edit fields, delete a non-reserved type, Reload.
- Requirement templates: Add row (dockerfile path under
etc/containers/dockerfiles/orkind: image+ref), set optional image_semver, Save templates, Build bundle, Reload. - In devtools Network tab, confirm
PUT/POST/DELETEreturn 2xx and response JSONok: truewhere applicable.