Skip to main content
Version: v3.9.0

Manifests

Edgelet accepts edgelet.iofog.org/v1 YAML manifests via:

edgelet deploy -f manifest.yaml
edgelet deploy -f manifest.yaml --timeout=20m # ControlPlane async apply

Agent daemon settings (`controllerUrl`, engine, limits): [Configuration](/learn/edgelet/configuration) - not deploy manifests.

Validation runs in the daemon before apply. Shapes are defined in internal/models/ and surfaced through EdgeletAPI /v1/deploy/*.

Example files: examples/

KindExampleGuide section
Microserviceexamples/microservice.yamlMicroservice
Registryexamples/registry.yamlRegistry
Modelexamples/model.yamlModel
Knowledgeexamples/knowledge.yamlKnowledge
RuntimeClassexamples/runtimeclass.yaml, examples/runtimeclass-edgelet-wasmtime.yamlRuntimeClass
ControlPlaneexamples/controlplane.yamlControlPlane

Common fields​

FieldValue
apiVersionedgelet.iofog.org/v1 (required)
kindMicroservice, Registry, Model, Knowledge, RuntimeClass, or ControlPlane

Legacy apiVersion: v3 and Java-era kinds are rejected.


Microservice​

Local or operator-managed workload deployed through Edgelet (not Controller snapshot).

Annotated reference: examples/microservice.yaml lists every YAML key with inline comments. Catalog bind lifecycle: Models, Knowledge. Engine coverage: Container engines.

Schema vs implemented​

FieldStatus
metadata.namespaceParsed; not used: runtime application is always edgelet
spec.configParsed; not applied
All other fields in the exampleApplied (healthCheck and annotations included)

containerEngine: edgelet and docker apply every container field below. podman reuses the Docker HostConfig mapping; cdiDevices is not wired on Podman. See Container engines.

Top-level shape​

apiVersion: edgelet.iofog.org/v1
kind: Microservice
metadata:
name: <dns-label> # required
namespace: edgelet # optional; use edgelet for local deploy scope
labels: {} # optional user labels (protected keys stripped)
spec:
image: <image-ref> # required
registry: <id> # optional registry row ID
models: { ... } # optional catalog bind - see below
knowledge: { ... } # optional catalog bind - see below
container: { ... } # see below
schedule: <int> # optional ordering hint
config: {} # optional opaque config map (not applied)

spec.models (catalog bind)​

Omit spec.models (or use an empty items list) when the workload does not bind artifacts. When items is non-empty:

FieldTypeNotes
bindPathstringRequired. Absolute container path. The catalog is one bind of a per-microservice projection at this path.
permissionsstringro (default) or rw. Catalog-level only: no per-item mode.
items[].namestringDNS-1123 Model metadata.name. Duplicate names are a validate error. Never send a host content path.

In the container, each item appears at {bindPath}/{name}/ and lists that model's Ready content/ files. Example: bindPath: /models + name: test-model → /models/test-model/.

bindPath and each {bindPath}/{name} must not collide with a volume containerDestination or tmpfs.containerPath.

Local deploy binds local models only. The container starts only when every named model and knowledge item is Ready; unknown or Failed names are a validate error; Pending/Pulling persist the workload as QUEUED with wait text. Add/remove/re-pull of items updates the projection in place (no recreate). Changing bindPath or catalog permissions does recreate. edgelet model rm is refused while any microservice still names that model.

spec.knowledge (catalog bind)​

Omit spec.knowledge (or use an empty items list) when the workload does not bind retrieval artifacts. When items is non-empty:

FieldTypeNotes
bindPathstringRequired. Absolute container path. The catalog is one bind of a per-microservice projection at this path.
permissionsstringro (default) or rw. Catalog-level only: no per-item mode.
items[].namestringDNS-1123 Knowledge metadata.name. Duplicate names are a validate error. Never send a host content path.

In the container, each item appears at {bindPath}/{name}/ and lists that Knowledge item’s Ready content/ files. Example: bindPath: /knowledge + name: product-docs → /knowledge/product-docs/.

bindPath and each {bindPath}/{name} must not collide with a volume containerDestination, tmpfs.containerPath, or spec.models.bindPath / {models.bindPath}/{modelName}.

Local deploy binds local Knowledge only. The start gate waits for every named model and knowledge item. Add/remove/re-pull of items updates the projection in place (no recreate). Changing bindPath or catalog permissions does recreate. edgelet knowledge rm is refused while any microservice still names that Knowledge. Operator guide: Knowledge.

spec.container (common fields)​

FieldTypeNotes
hostNetworkModeboolHost network: disables bridge DNS
isPrivilegedboolPrivileged container
runAsUserstringUser ID or name. Must not contain : when runAsGroup is set
runAsGroupstringGroup ID or name (separate from runAsUser)
readOnlyRootFilesystemboolApplied. Edgelet does not auto-inject /tmp; add a tmpfs at /tmp if the image needs it
runtimestringOCI runtime name (embed engine + RuntimeClass)
cdiDevices[]stringCDI device IDs (GPU, etc.); see Container engines
platformstringPlatform selector when pulling
ipcMode, pidModestringPassed to engine
capAdd, capDrop[]stringLinux capabilities
env{key,value}[]User env (EDGELET_* reserved)
extraHosts{name,address}[] or legacy strings/etc/hosts + docker ExtraHosts
ports{internal,external,protocol}[]Port mappings
volumes{hostDestination,containerDestination,accessMode,type,scope?}[]BIND, VOLUME, or controller VOLUME_MOUNT. On type: volume, scope is private (default) or shared. Omit / unknown → private. scope on BIND / volumeMount is ignored. Delete does not remove VOLUME data: see Volumes. Same scope field on controller volumeMappings[].
tmpfs{containerPath,size?,mode?}[]In-memory mounts. size is MiB. Absolute containerPath required
sysctlsstring mapKubernetes safe sysctls only (see allowlist below). hostNetworkMode: true rejects net.*. ipcMode: host rejects IPC-namespaced names (kernel.shm*, kernel.msg*, kernel.sem*, fs.mqueue.*). pidMode: host does not change sysctl validation
ulimitsmap of {soft,hard}Keys are Docker/RLIMIT names (see allowlist). -1 = unlimited. Nested cpu is RLIMIT_CPU (seconds), not cpus. If neither side is -1, soft must be <= hard; unlimited soft requires unlimited hard. Scalar values are rejected
devices{hostPath,containerPath,permissions?}[]hostPath must be under /dev. permissions is Docker-style r/w/m (default rwm)
entrypoint[]stringOmit or [] = image default ENTRYPOINT (empty argv is not sent to the engine)
commands[]stringOmit or [] = image default CMD. Controller JSON may send cmd as an alias of commands
workingDirstringAbsolute container working directory
cpuSetCpusstringcpuset
cpusfloatDocker --cpus (float CPU count). Not node cpuLimit percent
memoryLimitint64Memory limit (MiB)
memoryReservationint64Soft reservation (MiB). Allowed without memoryLimit
memorySwapint64-1 unlimited; else MiB memory+swap total (Docker --memory-swap). Requires memoryLimit unless -1
shmSizeint64/dev/shm size (MiB), not a generic tmpfs entry
annotationsmapApplied as container annotations
healthCheckobjectApplied. test argv; interval, timeout, startPeriod, retries in seconds

There is no per-microservice stopSignal. The image STOPSIGNAL and engine default SIGTERM apply.

Volume scope​

On type: volume, scope is private (default, per microservice UUID) or shared (node-global name under {diskDirectory}/volumes/shared/{name}/). Omit, empty, or unknown → private; apply does not fail. scope on BIND / VOLUME_MOUNT is ignored. The same field is on controller volumeMappings[]. See Volumes.

Sysctl allowlist​

kernel.shm_rmid_forced, net.ipv4.ip_local_port_range, net.ipv4.tcp_syncookies, net.ipv4.ping_group_range, net.ipv4.ip_unprivileged_port_start, net.ipv4.ip_local_reserved_ports, net.ipv4.tcp_keepalive_time, net.ipv4.tcp_fin_timeout, net.ipv4.tcp_keepalive_intvl, net.ipv4.tcp_keepalive_probes, net.ipv4.tcp_rmem, net.ipv4.tcp_wmem, net.ipv4.tcp_slow_start_after_idle, net.ipv4.tcp_notsent_lowat.

Ulimit allowlist​

core, cpu, data, fsize, locks, memlock, msgqueue, nice, nofile, nproc, rss, rtprio, rttime, sigpending, stack. Omit unused names.

Apply​

edgelet deploy -f examples/microservice.yaml
edgelet ms ls --source local
edgelet ms inspect <uuid-or-name>

edgelet ms inspect prints the full inspect JSON by default (models and knowledge catalogs plus raw.engineInspect). --summary is the short card. Wait/fail statusText is on the object when a bound model or Knowledge is still downloading or Failed. Crash fields: errorMessage (current; clears after 30s continuous RUNNING), lastError / lastErrorAt (last crash; not cleared on recovery), restartCount (omitted when 0). Docker/Podman text is exitCode=N oomKilled=…; the embedded engine keeps CRI reason=….

DNS: DNS · Metadata: Workload metadata


Registry​

Credentials for container image, model, and Knowledge artifact pulls, stored in local SQLite.

Annotated reference: examples/registry.yaml.

Built-in rows (cannot be edited or removed): id 1 docker.io (oci), id 2 from_cache (oci), id 3 https://huggingface.co (hf). User registries start at id 4.

apiVersion: edgelet.iofog.org/v1
kind: Registry
spec:
id: 10 # optional - upsert that local id; omit to allocate a new id
type: oci # oci (default) or hf
url: <registry-host> # required - host, or Hub/enterprise base URL
private: true|false # required
username: <string> # required when private=true and type=oci; optional for hf
password: <string> # required when private=true (Hub token when type=hf)
email: <string> # optional - oci only
ca: <base64-pem> # optional - extra CA bundle
insecure: false # optional - default false
FieldNotes
spec.idWhen set, upsert that row. When omitted, Edgelet allocates the next unused id after built-ins (4+). Ids 1-3 are refused. Same user id with a different (type, url) is a validate error.
spec.typeoci (default) or hf. Microservice image pull and edgelet image pull require oci.
spec.caBase64-encoded PEM. Applied to both oci and hf when set.
spec.insecureDefault false. When true, allow http:// URLs and skip TLS certificate verification for https://.
spec.emailRejected when type: hf.

Apply​

edgelet deploy -f examples/registry.yaml
edgelet registry ls
edgelet registry inspect 10

Registry apply is synchronous. edgelet registry ls / inspect show type and insecure (secrets are not printed unless --password-plain). Treat YAML as sensitive.


Model​

AI model artifact desired state. Pulls store files under {diskDirectory}/models/. Not through the container engine. Operator guide: Models. Packaging an OCI artifact: OCI artifacts.

Annotated reference: examples/model.yaml.

apiVersion: edgelet.iofog.org/v1
kind: Model
metadata:
name: llama-2-7b-q2k # required - DNS-1123 label; on-disk directory name
labels: {} # optional
spec:
repo: org/name # required - upstream path, no scheme or host
revision: <pin> # optional - see revision table
registry: 3 # required - registry row id (type selects the adapter)
files: # HF only; ignored for oci
- weights.gguf
format: gguf # optional - gguf, safetensors, onnx, pytorch, tensorrt, unknown
FieldNotes
metadata.nameLowercase DNS-1123 label (no /). Upsert key.
spec.repoHub repo id or OCI repository path without registry host.
spec.revisionOCI empty → latest; sha256: + 64 hex → digest; else tag. HF empty → main; 40-char hex → commit. Floating refs (latest, main, branches, tags) set revisionFloating: true and log a warning.
spec.registryRequired. Must exist; hf vs oci must match the source.
spec.filesHF only. Empty list → Hub snapshot at the pinned revision. Multi-*.gguf repos require an explicit list or glob. Globs: *, **, ?. Ignored for OCI (full artifact).
spec.formatHint only; does not change pull behavior.

Deploy apply persists the desired row and starts artifact download. The CLI waits until each model is Ready or Failed. --dry-run validates only. edgelet model pull <name> retries an existing row; with --repo and --registry it upserts the same row then pulls. Spec generation bumps also re-pull on reconcile.

Apply​

edgelet deploy -f examples/model.yaml
edgelet model ls
edgelet model inspect llama-2-7b-q2k

Knowledge​

Curated retrieval artifact desired state (documents, chunks, datasets, prebuilt vector indexes). Pulls store files under {diskDirectory}/knowledge/. Not through the container engine, and not in {diskDirectory}/models/. Operator guide: Knowledge. Packaging an OCI artifact: OCI artifacts.

Annotated reference: examples/knowledge.yaml.

apiVersion: edgelet.iofog.org/v1
kind: Knowledge
metadata:
name: product-docs # required - DNS-1123 label; on-disk directory name
labels: {} # optional
spec:
repo: org/name # required - Hub dataset id or OCI path, no scheme or host
revision: <pin> # optional - see revision table
registry: 3 # required - registry row id (type selects the adapter)
files: # HF only; ignored for oci
- data/**/*.jsonl
format: jsonl # optional - markdown, pdf, jsonl, parquet, arrow, sqlite, faiss, chroma, lance, unknown
FieldNotes
metadata.nameLowercase DNS-1123 label (no /). Upsert key. Separate namespace from Model names.
spec.repoHub dataset id or OCI repository path without registry host.
spec.revisionOCI empty → latest; sha256: + 64 hex → digest; else tag. HF empty → main; 40-char hex → commit. Floating refs (latest, main, branches, tags) set revisionFloating: true and log a warning.
spec.registryRequired. Must exist; hf vs oci must match the source. Hugging Face Knowledge always uses the Hub dataset API.
spec.filesHF only. Empty list → Hub dataset snapshot at the pinned revision (full tree). Globs: *, **, ?. Empty files on a huge dataset is allowed (operator choice); disk pre-check still applies. Ignored for OCI (full artifact).
spec.formatOptional hint only; does not change pull behavior. Omit allowed. Closed allowlist including unknown.

Deploy apply persists the desired row and starts artifact download. The CLI waits until each Knowledge is Ready or Failed. --dry-run validates only. edgelet knowledge pull <name> retries an existing row; with --repo and --registry it upserts the same row then pulls. Spec generation bumps also re-pull on reconcile.

Apply​

edgelet deploy -f examples/knowledge.yaml
edgelet knowledge ls
edgelet knowledge inspect product-docs

RuntimeClass​

Maps a handler name to OCI runtime configuration on containerEngine: edgelet (linux embed only).

Naming: containerEngine: edgelet is the embedded engine product. WASM workload runtimes use distinct handler keys. For example edgelet-wasmtime (Datasance PoT shim, io.containerd.edgelet.v2) vs upstream wasmtime (io.containerd.wasmtime.v1). See examples/runtimeclass-edgelet-wasmtime.yaml.

apiVersion: edgelet.iofog.org/v1
kind: RuntimeClass
metadata:
name: <dns-label> # required; lowercase DNS label (e.g. edgelet-wasmtime)
handler: <handler> # required; catalog handler (e.g. edgelet-wasmtime, spin)

Reserved name: crun (built-in default).

Apply​

edgelet deploy -f examples/runtimeclass.yaml
edgelet runtimeclass ls

Reference microservice spec.container.runtime to the RuntimeClass name. See Container engines.


ControlPlane​

Deploys one Controller container per Edgelet node (optional. Remote controllerUrl is valid without local ControlPlane).

Annotated reference: examples/controlplane.yaml lists every YAML key (active + commented optional blocks).

Required fields​

FieldNotes
spec.controller.imageController container image
spec.auth.modeembedded or external
spec.auth.bootstrapRequired when mode: embedded (username, password with complexity rules)
spec.auth.issuerUrl + spec.auth.clientRequired when mode: external

Forbidden fields​

FieldReason
metadata.labelsRejected at validate
spec.siteCA, spec.localCAImport via Controller REST after deploy
apiVersion: edgelet.iofog.org/v1
kind: ControlPlane
metadata:
name: <ms-name> # required; DNS-1123 label
namespace: <namespace> # optional; default applied if empty
spec:
controller:
image: <image> # required
registry: <id> # optional
port: 51121 # optional API port
publicUrl: <url> # recommended - CONTROLLER_PUBLIC_URL
trustProxy: true|false # optional
console:
port: 8008 # optional - host port 80 maps here
url: <url> # optional - CONSOLE_URL
auth: # required
mode: embedded|external
insecureAllowHttp: false
insecureAllowBootstrapLog: false
bootstrap: # required when mode=embedded
username: admin
password: "<secret>" # ≥12 chars, 1 uppercase, 1 special
issuerUrl: <url> # required when mode=external
client: # required when mode=external
id: <id>
secret: <secret>
rateLimit: { enabled, maxRequestsPerWindow, windowMs }
sessionStore: { type, ttlMs, secret }
tokenTtl: { accessTokenTtlSeconds, refreshTokenTtlSeconds }
oidcTtl: { interactionTtlSeconds, grantTtlSeconds, sessionTtlSeconds, idTokenTtlSeconds }
systemMicroservices: # optional router/nats image maps per arch
router: { amd64: "...", arm64: "..." }
nats: { ... }
nats:
enabled: true|false
# events, database, tls, vault, logLevel - see control-plane.md

Rules​

  • metadata.labels forbidden on ControlPlane manifests.
  • spec.siteCA / spec.localCA forbidden. Import CAs via Controller REST after deploy.
  • At most one ControlPlane row per node; delete via edgelet controlplane delete.

Apply (async)​

edgelet deploy -f examples/controlplane.yaml
edgelet controlplane get

Default poll budget 15 minutes. See Local control plane.

DNS identity​

FQDNs derive from metadata.namespace + metadata.name. See DNS.


CLI quick reference​

ActionCommand
Apply manifestedgelet deploy -f <file>
List local MSedgelet ms ls --source local
List registriesedgelet registry ls
List modelsedgelet model ls
List knowledgeedgelet knowledge ls
List runtime classesedgelet runtimeclass ls
Control plane statusedgelet controlplane get
Validate onlyEdgeletAPI POST /v1/deploy/microservices:validate (and :validate for other kinds)

Group 3See anything wrong with the document? Help us improve it!