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.
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
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
You are ready to add networking when you can explain which layer owns the interface, route, and listener you are about to change.