Will my image run?
Updated
Most images that work under Docker work as an AppVM unchanged. This page is the check to run before you import one, and the explanation when something exits straight away.
Quick check
Section titled “Quick check”- Service images such as nginx, redis, postgres, or your own service: import and run. Remember to attach a volume for anything that must persist.
- Base and runtime images such as
alpine,debian,ubuntu,node, orpython: these exit immediately. Their entrypoint is an interactive shell or REPL with nothing attached to it. Turn on keepalive if you want to stay inside and poke around, or use an image with a real entrypoint to run a service. - Non-root images run fine. To write to a data volume, the image must already contain that directory owned by the image’s user.
- Distroless and other images with no shell: the workload runs. What you
lose is everything that needs
/bin/sh, which means no web terminal, no keepalive, no shell-form entrypoint, and no shell-form health check. Reading and writing the machine’s files, and starting a program by name, do not need a shell, and neither does a TCP or HTTP health probe. - An image whose entrypoint is an init,
systemdor an equivalent: the machine boots it and it runs its own services, as covered in Init images and container runtimes. - A container runtime as the workload (
containerd, podman, k3s): the guest mounts cgroup2 and offers the namespace abilities a runtime checks for before it starts. What was measured is spelled out in Init images and container runtimes.
Image families
Section titled “Image families”| Image | Runs | What to know |
|---|---|---|
| Alpine and derivatives | Yes | A bare alpine entrypoint is a shell, so it needs keepalive |
| Debian / Ubuntu | Yes | Bare images exit immediately and need keepalive; service images are fine. The image’s own /etc/hosts is empty; the guest writes one at every boot |
| nginx | Yes | Runs unchanged. Its declared stop signal is honoured, so shutdown drains gracefully |
| PostgreSQL | Yes, with care | See the note below about PGDATA |
| Redis | Yes | Attach a volume at /data for persistence. A very large save may not finish inside the stop grace period |
| Node / Python | Yes | Service images are fine; a bare node or python3 entrypoint is a REPL and exits |
| Go distroless | Yes, with limits | Runs, including a numeric user, but has no shell |
| Images with a health check | Yes | Probing and status work. Automatic recovery follows the restart policy, which starts at no |
| Images that fork children | Yes | Child processes are reaped and signals reach the group |
| Images that ignore SIGTERM | Yes | Escalates to a forced stop after the grace period |
| An image whose entrypoint is an init | Yes | systemd boots and runs its own services. See Init images and container runtimes |
| A container runtime as the workload | Yes | containerd, podman, and k3s all look for the cgroup mount the guest now provides; k3s is measured |
Volume ownership
Section titled “Volume ownership”A brand new volume takes its owner and mode from the directory the image ships at that mount point. If the image does not contain the directory, the mount point is created owned by root, and a non-root workload will not be able to write to its own volume.
Two ways out: use an image that ships the directory with the right owner, or use an image whose entrypoint starts as root and adjusts ownership itself, which is what the postgres and redis images do.
Init images and container runtimes
Section titled “Init images and container runtimes”An AppVM is one image with one main process, and it is not a container engine: Virtainer Free does not schedule several containers inside one machine, and an image’s own processes share that machine’s single filesystem and network. What the guest does provide is a normal Linux kernel with the things a container runtime asks for before it starts.
The guest mounts a unified cgroup hierarchy. A runtime looks for that path
before it does anything else, and an empty /sys/fs/cgroup made it quit on the
spot with an error that reads like missing kernel support. The kernel has always
had the ability; the mount was what was missing.
A template no longer carries the build container’s /run. Flattening an image
used to pull in that directory’s leftovers, which are runtime mounts rather than
image content. One of them, /run/systemd/notify, is a directory where systemd
wants a socket, so an init-style entrypoint took an address-in-use error and froze
on the spot: the machine showed as running, the log was one line long, and nothing
was up. Image authors cannot work around it, because those paths are live mounts at
build time and refuse to be deleted. The host now clears /run when it converts an
image, so templates you convert from here on are clean. A template you imported or
built earlier still holds what it was given: rebuild or re-import it to apply the
change, then redeploy the machines that use it.
What that has been measured with, on a real host:
- A single-node k3s reached
Readyusing the image’s own command, with no wrapper entrypoint, and a pod on the flannel network produced logs throughkubectl. The same image previously died right after signing its certificates. - A four-node Kubernetes cluster built with
kubeadm, on Ubuntu 24.04 images with systemd managing the kubelet and one node per AppVM, reachedReadyon all four nodes. Before the/runchange, systemd froze as the first step.
Two things to expect if you take this on:
- An exec terminal starts in the machine’s root process tree. An init-style
workload puts the services it manages into namespaces of their own, so talking
to that init from a terminal is not always direct: on the cluster above,
systemctlonly answered once it was run against the init’s process throughnsenter. - A container runtime pulls the images it needs over the network, from inside the machine, so it depends on that machine’s own network and egress rather than on what this host already imported.
Not supported
Section titled “Not supported”-
Automatic anonymous volumes. A
VOLUMEdeclaration does not create a volume for you. Persistent data needs an explicitly attached volume. The create form does warn when the image declares a path as persistent and you have not attached anything to it. -
Images built for a platform other than
linux/amd64. Admission names the platform it found and says which one it needs. -
Health checks declared by OCI-format images. The format does not carry the field. You can add one yourself when you create the machine.
-
Interactive stdin and TTY entrypoints. Keepalive is an escape hatch for debugging, not a way to run an interactive program as a service.
-
Several containers as several units in one machine. One image is one machine, with one main process that owns its health and its result. Sidecars you declare are extra processes inside that one environment, not separate containers with their own disk, network, or restart policy. Running a container runtime as your workload is a different thing, and it works.
A machine takes up to eight sidecars. Each needs a name of 1 to 32 lowercase letters, digits or hyphens, starting and ending with a letter or digit, and
mainis reserved for the workload itself. Each declares its own command; it inherits the image’s environment, working directory and user, so a redeploy onto a newer build re-inherits those from the new image. An older guest does not understand named sidecars, and the machine does not quietly start without them: it fails to boot withFATAL [capability] guest did not acknowledge named sidecars within 10 seconds. Redeploy or reboot it to bring the guest up to date. -
Virtual machines inside an AppVM. The guest has no
/dev/kvm, which is deliberate: a container runtime inside a machine is supported, a hypervisor inside it is not.
Stop behaviour
Section titled “Stop behaviour”If the image declares a stop signal, that is what gets sent, so postgres gets its fast-shutdown signal and nginx gets its graceful-drain signal. The grace period defaults to 10 seconds and can be raised to 300 when you create the machine. After it expires the workload is killed and the machine is torn down.