Isolated From What, Exactly: Containers, Images, and Their Boundaries
A team moves a batch job into a container and someone asks whether it is safe to run a customer’s uploaded script in it. The answer given is “yes, it’s containerized, it’s isolated.” Six months later an audit asks which kernel vulnerabilities that arrangement was exposed to, and nobody has an answer, because nobody had asked what the container was isolated from.
That question is the subject of this article, and the honest answer is more specific and more useful than “isolated.” A container is isolated in the particular dimensions it was given isolation for, limited in the particular resources it was given limits for, and shares everything else with the host — starting with the kernel. Once that is clear, both the legitimate uses and the boundaries follow.
Descriptions below come from the Linux kernel manual pages for the mechanisms and from the Open Container Initiative specifications for the formats, read in September 2026. The kernel gains namespace types over time and the specifications are versioned, so treat the lists as a reading from that date.
Isolation is per-dimension, and opt-in
The mechanism is the Linux namespace, and the kernel’s own definition is worth reading closely because it explains the shape of everything else: “a namespace wraps a global system resource in an abstraction that makes it appear to the processes within the namespace that they have their own isolated instance of the global resource. Changes to the global resource are visible to other processes that are members of the namespace, but are invisible to other processes.” The manual page adds, almost in passing, that “one use of namespaces is to implement containers.”
Note the singular in “a global system resource.” A namespace does not isolate a process in general; it isolates one kind of thing. The kernel documents eight types.
| Namespace | What it isolates |
|---|---|
| PID | “Process IDs” |
| Network | “Network devices, stacks, ports, etc.” |
| Mount | “Mount points” |
| User | “User and group IDs” |
| IPC | “System V IPC, POSIX message queues” |
| UTS | “Hostname and NIS domain name” |
| Time | “Boot and monotonic clocks” |
| Cgroup | “Cgroup root directory” |
Each is requested separately — they are flags on process creation, one CLONE_NEW* per type. Which means a container’s isolation is a set of choices, and a runtime that does not create a user namespace has given you a container whose user IDs are the host’s user IDs. That is the single most consequential of the eight, and the reason “the process inside runs as root” is a question worth asking rather than assuming.
Resource limits are a different mechanism with a different name. Control groups “allow processes to be organized into hierarchical groups whose usage of various types of resources can then be limited and monitored,” where “a cgroup is a collection of processes that are bound to a set of limits or parameters defined via the cgroup filesystem.” The limiting is done by per-resource subsystems, making it possible “to do things such as limiting the amount of CPU time and memory available to a cgroup, accounting for the CPU time used by a cgroup, and freezing and resuming execution of the processes in a cgroup.”
So the two halves answer different questions. Namespaces decide what a process can see. Cgroups decide how much it can consume. A container with namespaces and no cgroup limits is invisible to its neighbours and perfectly able to starve them of memory — which is exactly the failure that gets described as “the container took down the host.”
One terminology warning, because it causes real confusion later in this subject. The namespaces here are a kernel isolation mechanism. Kubernetes also has something called a namespace, which is a logical grouping for organizing objects and applying policy, and the two are unrelated. When both appear in the same conversation, say which you mean.
What is not isolated, and what that means for virtual machines
The comparison with virtual machines is usually made about weight — containers start faster and use less memory — which is true and not the part that matters for design. The structural difference is that containers on one host share one kernel, and virtual machines do not.
Everything follows from that. A kernel vulnerability is a vulnerability for every container on the host. A kernel parameter is, with limited exceptions, a host-wide setting. A kernel panic takes down every container. And the isolation quality is the quality of the kernel’s own enforcement of those namespace and cgroup boundaries, which is strong in practice and not the same kind of boundary as a hypervisor’s.
Which gives a usable decision rule rather than a preference. Containers are a strong boundary between things you operate; a hypervisor is the boundary you want between things you do not trust. Running your own services next to each other in containers is ordinary and sensible — that is workload isolation at a reasonable cost. Running untrusted customer-supplied code in a plain container, as in the opening scenario, is putting weight on a boundary that was not designed for adversaries. Where that is the requirement, the answer is a stronger sandbox — a virtual machine per tenant, or a runtime built specifically to interpose on system calls — and the same reasoning applies to tenant isolation in a multi-tenant product.
Worth knowing that the specifications do not treat these as opposites. The runtime specification’s list of defined platforms includes linux and windows and also vm, so “a container” and “a virtual machine” name a packaging-and-lifecycle contract and an isolation technology respectively, and some runtimes satisfy the first using the second. The useful question is never “container or VM” but “what is the isolation boundary, and is it strong enough for what is on the other side.”
An image is a stack of changes, not a snapshot
A container image is defined by specification rather than by a product. The OCI image format “defines an OCI Image, consisting of an image manifest, an image index (optional), a set of filesystem layers, and a configuration,” with the goal “to enable the creation of interoperable tools for building, transporting, and preparing a container image to run.”
Four parts, each with a distinct job.
- The manifest holds “metadata about the contents and dependencies of the image including the content-addressable identity of one or more filesystem layer changeset archives that will be unpacked to make up the final runnable filesystem.”
- The layers are those changeset archives — the filesystem content, in pieces.
- The configuration “includes information such as application arguments, environments, etc.” — what to run and with what defaults.
- The index, when present, is “a higher-level manifest which points to a list of manifests and descriptors,” typically providing “different implementations of the image, possibly varying by platform.” This is why one name can serve both x86 and ARM machines.
Now the part that changes how you treat images. A layer is not a copy of a filesystem; it is a changeset. The layer specification says so directly: it “describes how to serialize a filesystem and filesystem changes like removed files into a blob called a layer. One or more layers are applied on top of each other to create a complete filesystem.” The change types are “Additions, Modifications, Removals,” and the crucial detail is how the third is represented: “additions and modifications are represented the same in the changeset tar archive. Removals are represented using ‘whiteout’ file entries.“
A removal is therefore a note saying “this path is gone,” recorded in a later layer. The bytes in the earlier layer are still there, still distributed, still readable by anyone who can pull the image and inspect its layers.
Which turns a common build pattern into a leak. Copy a credentials file, use it, delete it in a later step, and you have shipped the credential — the deletion is a whiteout entry sitting above it. The same applies to anything sensitive that passed through the filesystem during a build: source archives, private keys, tokens in a configuration file. The remedy is not to delete more carefully but to never add it in the first place, which is the general rule in secrets management appearing here in a specific and easily overlooked form. And if it has already shipped, the only real fix is to rotate the credential.
Names, digests, and the registry
The specification’s summary of an image’s life is a compact description of what a registry is for: “once built the OCI Image can then be discovered by name, downloaded, verified by hash, trusted through a signature, and unpacked into an OCI Runtime Bundle.”
Read the sequence: discovered by name, verified by hash. Those are two different identifiers and only one of them is stable. A tag is a mutable pointer — 2.4.1 can be pushed again tomorrow at different content — while the content-addressable digest names exactly one set of bytes and cannot be reissued. So deployments should refer to the digest and let the tag be a convenience for humans, which is the same argument made about any build artifact and is easier to get wrong here because tags are so convenient.
Two practical consequences of the layer structure are worth knowing while you are here. Layers are shared: two images built on the same base transfer and store that base once, which is why base image choice affects pull times across a fleet. And layer order determines cache behaviour — anything that changes often should sit above anything that changes rarely, or every build re-transfers everything below the change.
Because the digest verifies content and signatures can be attached, an image is a reasonable unit for supply-chain controls — verifying what you are about to run rather than trusting a name. That is the subject of software supply chain security, and where you need two builds of the same source to produce the same bytes, see reproducible build.
What the image decides, and what run time decides
The configuration inside the image supplies defaults — arguments, environment, the command to run. Everything about a particular execution is supplied when the container is created, and the split matters for the same reason it matters for any deployable artifact: the image should be the thing you tested, and only the surrounding values should differ between environments.
| Baked into the image | Supplied at run time |
|---|---|
| The filesystem: binaries, libraries, assets | Configuration values — endpoints, feature settings |
| The default command and arguments | Secrets, never in the image |
| Declared environment defaults | Mounted storage, and therefore anything durable |
| The platform it is built for | Namespaces and cgroup limits — that is, the isolation and the resource ceiling |
The last row is the one that surprises people, and it is the point of this whole article: an image contains no isolation. Isolation and limits are arguments to the act of running, so the same image is safe or unsafe depending on how it is launched. An audit of container security that examines images and not run-time configuration has examined the wrong half.
On the second row: passing a secret as an environment variable keeps it out of the image, which is the important win, and it is not the end of the matter — process environments appear in diagnostics and in anything that dumps state. Keeping the image clean is necessary and not sufficient.
Four things containers do not solve
- Dependency correctness. Packaging a broken dependency set reproducibly gives you a reliably broken system. The image makes the set fixed, not right.
- Patching. The libraries in an image age from the moment it is built, and nothing inside the container updates them. Currency comes from rebuilding and replacing, which is the operational practice covered under golden image applied to a different kind of image.
- Privilege. A process that runs as root inside a container, without a user namespace, is running as a root the host recognizes. Containerizing does not apply least privilege on your behalf.
- State. A container’s filesystem is disposable by design. Anything that must survive lives on mounted storage, and treating a container as a place to keep things is how data is lost on the first redeploy.
None of that argues against containers. What they genuinely give you is a packaging format with a stable identity and a run-time contract that is the same everywhere, which is a large win and the reason the industry standardized on it. The point is that the security properties are a product of how you run them, and the currency properties are a product of how often you rebuild — neither arrives with the format.
So a question worth being able to answer about any container in your estate, and the one the opening scenario could not: which namespaces does it have, what are its cgroup limits, what user does it run as, and what is on the other side of that boundary? Four answers, all determined at launch, none visible in the image.
References: Linux manual page, namespaces(7); Linux manual page, cgroups(7); OCI Image Format Specification; OCI Image Layer Filesystem Changeset; OCI Runtime Specification. All read September 2026.
Discover more from Insightful Data Lab
Subscribe to get the latest posts sent to your email.
