The Docker Alternative
Docker is the undisputed king of application containerization. It is designed to package a single microservice (like a Node.js app or a Python script) and run it in an isolated sandbox. However, Docker is fundamentally an application container. It does not boot a full Linux operating system; it does not run systemd, it doesn’t have an SSH daemon, and it doesn’t behave like a real server.
If an administrator needs to provision 50 distinct Ubuntu environments—each with its own IP address, background services, and root filesystem—deploying 50 full KVM virtual machines is incredibly wasteful, consuming massive amounts of RAM and CPU overhead.
The solution is LXD/LXC (Linux Containers). LXC provides system containers. An LXC container feels, acts, and boots exactly like a full KVM virtual machine. You can SSH into it, install packages via apt, and configure systemd services. However, because it shares the underlying kernel with the host, it incurs virtually zero overhead. You can run 50 LXC containers on a laptop without breaking a sweat. Crucially, modern LXC runs unprivileged by default, meaning even if a hacker gains root access inside the container, they are still mathematically mapped to a standard, non-root user on the host system, trapping them securely in the sandbox.
Step 1: Initializing the LXD Daemon
In modern Ubuntu, the backend daemon that manages containers is lxd, while the command-line client you type into the terminal is lxc.
LXD is typically pre-installed via Snap on Ubuntu Server. Before creating a container, you must initialize the daemon to configure the storage backend and the virtual network switch.
sudo lxd init
The wizard will ask you a series of questions. For a standard setup:
- Clustering: No.
- Storage Pool: Create a new default pool. (ZFS or Btrfs are highly recommended, as they allow instantaneous container cloning via Copy-on-Write).
- Network: Create a new bridge (
lxdbr0). This will automatically configure a hidden DHCP server and NAT routing, allowing your containers to reach the internet.
Step 2: Launching Your First Container
Once the daemon is initialized, launching a full Ubuntu server takes approximately 3 seconds.
Use the lxc launch command, followed by the image repository, the OS version, and the name you want to assign to the container (e.g., web-server-01):
lxc launch ubuntu:22.04 web-server-01
The client reaches out to the canonical image server, downloads the compressed root filesystem (only a few hundred megabytes), unpacks it into the ZFS storage pool, and boots the systemd initialization process.
To view the status of your running container, including its dynamically assigned IPv4 address:
lxc list
The output is a clean table showing the container state (RUNNING) and its IP address (e.g., 10.205.10.55).
Step 3: Entering the Container
You do not need to configure SSH to get inside the container. Because LXD controls the kernel namespaces, you can forcefully inject a bash shell directly into the running container from the host.
lxc exec web-server-01 -- /bin/bash
Your terminal prompt will instantly change from ubuntu@host to root@web-server-01. You are now inside the container. You can run apt update, install Nginx, and configure the system exactly as you would a bare-metal server. When you are finished, simply type exit to return to the host.
Step 4: Resource Limits (Cgroups)
By default, the container has access to all the CPU and RAM available on the host machine. If you install a database inside the container and it suffers a memory leak, it will crash the host.
You must apply hardware limits. LXC uses Linux Control Groups (cgroups) to enforce these limits on the fly, without requiring a container reboot.
To restrict web-server-01 to exactly 2 CPU cores and 1 Gigabyte of RAM:
lxc config set web-server-01 limits.cpu 2
lxc config set web-server-01 limits.memory 1GB
If you execute a bash shell inside the container and run the htop command, the container will literally believe it is running on a 2-core machine with 1GB of RAM. The illusion is perfect at the kernel level.
Step 5: Snapshots and Instant Cloning
If you initialized LXD using ZFS or Btrfs, managing containers becomes incredibly powerful.
Suppose you just spent three hours perfectly configuring the Nginx web server inside web-server-01. Before deploying your PHP code, you want to take a backup.
lxc snapshot web-server-01 pre-deployment
This takes a fraction of a second. If you deploy the PHP code and the server crashes, you can instantly rewind time:
lxc restore web-server-01 pre-deployment
Furthermore, you can clone the entire server to create a testing environment. You do not need to download the OS again. LXC uses ZFS block cloning to instantly duplicate the container:
lxc copy web-server-01 web-server-test
You now have a perfect replica of the Nginx server running in parallel, with its own unique IP address, consuming zero additional hard drive space until you begin modifying files inside the clone.
Conclusion
While Docker remains the standard for stateless microservices, deploying a full, multi-service operating system architecture requires a different tool. By mastering the lxc command, Ubuntu administrators can provision dozens of secure, unprivileged system containers. LXC combines the familiar management experience of a KVM virtual machine with the zero-overhead, bare-metal performance of kernel namespaces, completely redefining dense server virtualization.