container {} block
Generated Markdown for references/entity_container_block.md.
Open book page Back to the skill graph
# container {} block
_wcl block_
Declares an OCI container in a lab: image reference, env/volumes/ports/healthcheck — run in a micro-VM on the same segments as VMs.
A `container {}` block inside a `lab {}` runs a standard OCI container image
(docker-style: `nginx:1.27`, `ghcr.io/owner/app@sha256:…`; Docker Hub shorthand
is normalised) alongside the lab's VMs. Under the hood the container boots in a
tiny micro-VM (pinned Alpine kernel + purpose-built init), so it works in the
official vmlab container image with no extra privileges — but from the lab's
point of view it is just another machine (PRD §18).
```wcl
container "web" {
image = "nginx:1.27"
profile = "container" // micro-VM hardware floor
memory = 512MiB // overrides the profile
depends_on = ["db"] // VM or container names — one namespace
nic { segment = "corp" ip = "10.50.0.20" }
env { name = "MODE" value = "prod" }
volume { name = "data" target = "/var/lib/data" } // named, lab-scoped
volume { host = "./www" target = "/srv/www" read_only = true }
port { host = 18080 container = 80 } // host → container
healthcheck { command = ["curl", "-fsS", "http://localhost/"] interval = 5s }
}
```
```wcl
container "toolbox" {
image = "ubuntu:24.04"
profile = "container"
mode = :idle // do not run the image entrypoint/cmd
}
```
VM and container names share one namespace: DNS registration
(`web.<lab>.<suffix>` and plain `web`), `depends_on` waves, segment
`forward { to = "web:80" }` targets and provision scoping resolve across both
kinds. NICs are optional — a container with none is air-gapped but still
reachable with `vmlab container exec` / `cp` over the agent channel. The image
digest resolved at first pull is pinned in lab state (never re-pulled
implicitly); `vmlab container destroy` or editing `image =` clears the pin.
Readiness gates on the process starting plus the first passing healthcheck;
an exited process remains stopped until it is explicitly started. Containers
snapshot with **full VM parity** — offline and online, standalone or as part of a lab-wide
`vmlab snapshot` — and volumes ride virtiofs (CIFS fallback), like
[shares](../references/entity_shares.md). Named volumes survive `down` and per-container
destroy; lab `destroy` removes them.
**Sizing the micro-VM.** `cpus` and `memory` resolve through the same
precedence chain as a VM's — declaration first, then the named `profile` (a
container has no template layer). There is no built-in default, because what a
micro-VM needs depends entirely on its image: a container that neither
declares a size nor names a profile supplying one fails `vmlab validate`,
rather than silently booting into a guess it will OOM under. The shipped
`container` profile carries the conservative floor (1 vCPU, 256MiB); drop your
own into `~/.config/vmlab/profiles/` to change it lab-wide.
With `mode = :idle`, the root filesystem, volumes, networking and guest agent
come up without starting the image entrypoint or command. Readiness gates on
the agent instead, image healthchecks do not run, and commands can be launched
with `vmlab container exec` or from provision scripts.
## Related
- [Lab containers](../references/concept_lab_containers.md)
- [lab {} block](../references/entity_labs.md)
- [vm {} block](../references/entity_vms.md)
- [nic {} block](../references/entity_nic_block.md)
- [segment {} block](../references/entity_segment_block.md)
- [Container handle](../references/entity_container_api.md)
[← Back to SKILL.md](../SKILL.md)