You've run “docker run” a hundred times. But what is a container image, and why does the exact same one run on Docker, on a Kubernetes node, and now inside a tiny VM on a Mac? This talk treats the image as a file you can open, and the runtime as one of several programs that can read that file.
Live on stage, I build one image and run it unchanged on three open-source runtimes:
Docker Engine (Moby, running inside a Lima VM - not Docker Desktop)
containerd via nerdctl, which is what Kubernetes actually runs
apple/container - Apple's Apache-2.0 runtime that gives every container its own lightweight VM
Then I crack the image open. “docker save” gives a tarball; “tar -xf” and “jq” show what's inside: “index.json”, a manifest, a config blob (Dockerfile, frozen: env, entrypoint, `rootfs.diff_ids`) and layer blobs that are plain tarballs of filesystem diffs. “mediaType: application/vnd.oci.image.manifest.v1+json” show the spec.
The image never touches a registry or the network. It moves between runtimes as a tarball by hand, which is itself the point.
Outline (27 min)
0-2: hook. “docker run”, “kubectl run”, “container run”. Same image, three programs, nobody coordinated. Why does this work?
2-6: build once. Five-line Alpine Dockerfile that prints “hostname”, “/etc/os-release”, “uname -r”. “docker build”, “docker save”.
6-13: run thrice. Load the tarball into containerd and apple/container, run all three in a three-pane terminal. Note the kernel and hostname differences, park them.
13-20: crack it open. “tar”, “tree”, “jq” through index, manifest, config, layers. Optional sha256 check.
20-25: Why runtimes differ. Three columns: shared host kernel (namespaces + cgroups), one shared Linux VM (Lima / Docker Desktop), one VM per container (apple/container, same idea as Kata on Linux). Shared kernel is fast and dense; VM-per-container is stronger isolation. OCI lets us choose the runtime without touching the image; “RuntimeClass” in Kubernetes.
25-27: three takeaways and a call to action