A container lab is easier to debug when you know which layer owns the failure. “The container can’t reach the network” could mean a problem with its interface, the VM’s forwarding rules, or the desktop’s connectivity.

We’ll use a Linux VM as a disposable LXC host and inspect one unprivileged container. The old Vagrant, Fabric, and web-panel bootstrap from this post has been retired; its automatic defaults hid too much of the setup.

Name the three layers

Your physical host runs a hypervisor. The hypervisor runs a Linux VM. LXC creates containers using that VM’s kernel.

Each layer has a different job. The VM can reboot independently of the physical host. Containers inside it can have separate process and filesystem views, but they depend on the VM’s running kernel.

Use the nested rectangles to predict the effect of stopping each layer.

A host inside a hostIn this nested arrangement, containers share the virtual machine’s Linux kernel. The virtual machine provides a separate boundary from the physical host. A host inside a host Physical hostVirtual machineOne shared guest Linux kernelContainer AIsolated userspaceContainer BIsolated userspace
In this nested arrangement, containers share the virtual machine’s Linux kernel. The virtual machine provides a separate boundary from the physical host.
View full-size diagram (opens in a new tab)

Prepare the host deliberately

Create a VM using a supported Linux distribution and the hypervisor you already understand. Keep it disposable and take a clean snapshot if your tooling supports one.

Install LXC using the distribution’s packages and run:

lxc-checkconfig

This inspects kernel support. A favorable report does not configure subordinate user IDs, networking, or delegated cgroups for you. A cgroup is a kernel mechanism for organizing and controlling resource use by processes.

Follow the LXC getting-started guide for your distribution’s unprivileged-container setup. That includes user/group mappings and, where required, cgroup delegation. Select an image supported by the download template rather than copying a historical distribution release.

An unprivileged container maps its root identity to a non-root identity outside the container. That narrows authority, but it doesn’t remove the shared kernel. The LXC security documentation explains the boundary and remaining risks.

Work through the inspection

After creating and starting a test container named lesson, use the same account and delegation wrapper used to start it:

lxc-info --name lesson
lxc-ls --fancy
lxc-attach --name lesson --clear-env

These commands assume the container already exists. Inside the attached shell, run:

uname -r
cat /etc/os-release
cat /proc/self/uid_map

Compare the kernel release with the Linux VM. Inspect the UID map instead of assuming that a root prompt means host root. Mapping entries describe an inside ID, an outside ID, and a range length.

For example, a conceptual mapping of 0 100000 65536 maps container IDs starting at zero to outside IDs starting at 100000. It doesn’t grant the container the outside root identity.

Pause and predict

You stop the Linux VM. Which containers keep running inside it?

Need a hint?

All their processes depend on that VM's kernel.

Show the reasoning
None. Process isolation within a guest doesn’t make the guest optional. Rebuilding or restarting the VM also requires a deliberate policy for restarting its containers.

Change one boundary

First verify a command inside the container. Next verify a local service inside it. Only then test access from the VM and from another machine.

Keep a simple record:

Observation Next boundary to inspect
Local service fails inside container Application or container configuration
Local request works, VM request fails Bind address and container networking
VM request works, remote request fails VM routing, forwarding, or host firewall

This table is a diagnosis order, not proof that a single component must be at fault.

Try it yourself

Stop only the test container and confirm that the VM remains usable. Start it again and verify the same application result. Then stop it and remove it with lxc-destroy --name lesson when you no longer need its data.

Apply the idea

A container cannot create a device node. Should you immediately make it privileged?

Need a hint?

First ask whether the application actually needs that device operation.

Show the reasoning
No. Establish the required operation and inspect the permission boundary. You may be able to remove the requirement or provide a narrowly scoped alternative. Making the whole container privileged changes far more than the one failing call.

You are ready to add networking when you can explain which layer owns the interface, route, and listener you are about to change.