LXC and LXD
LXC and LXD provide full Linux system containers — a complete operating system (init system, services, network stack) inside an isolated environment that shares the host kernel. This sits between application containers (Podman) and full virtual machines (QEMU/KVM) in terms of isolation and overhead.
On Shanios, LXC and LXD are pre-installed with dedicated Btrfs subvolumes (@lxc and @lxd). lxd.socket is socket-activated. lxcfs.service is enabled — it virtualises /proc/cpuinfo, /proc/meminfo, and similar files so containers report per-container values rather than host totals. Containers survive every OS update and rollback untouched.
When to Use LXD
| | Distrobox | Podman | systemd-nspawn | LXD | QEMU/KVM | |---|---|---|---|---|---| | Purpose | Dev/app containers | OCI app containers | Lightweight system containers | Full system containers | Full VMs | | Shares home dir | Yes | No | No | No | No | | Full init system | No | No | Yes | Yes | Yes | | Startup time | ~1s | ~0.1s | ~1s | ~3s | ~30s | | Daemon required | No | No | No | Yes (socket) | Yes | | Isolation | Low–medium | Medium | High | High | Full |
Use LXD when:
- You need a complete isolated server environment (web server, database, multiple services with init)
- You want strong isolation without the overhead of a full VM
- You need LXD's image catalog, built-in port forwarding devices, or remote container management
- You want Btrfs-backed container snapshots managed via
lxc snapshot
Use systemd-nspawn when you want the same full-system isolation with less setup — no init wizard, no daemon, no image format. See systemd-nspawn.
Setup
lxd.socket is socket-activated on Shanios. Your user is automatically added to the lxd group during installation. Initialize LXD on first use:
# Interactive wizard (choose btrfs as storage backend)
sudo lxd init
# Or use defaults
sudo lxd init --auto
# Verify group membership
groups | grep lxd
# If missing:
sudo usermod -aG lxd $USER
# Log out and back in
When the wizard asks about the storage backend, choose btrfs — it integrates with the host Btrfs filesystem and maps into the @lxd subvolume.
Creating and Managing Containers
# List available images
lxc image list images: | grep -i ubuntu
lxc image list images: | grep -i alpine
lxc image list images: | grep -i debian
# Launch a container (downloads image if needed)
lxc launch ubuntu:24.04 myubuntu
lxc launch debian:bookworm mydebian
lxc launch alpine:3.19 myalpine
lxc launch archlinux:current myarch
# List containers
lxc list
# Open a shell
lxc exec myubuntu -- bash
# Run a specific command
lxc exec myubuntu -- apt update
lxc exec myubuntu -- systemctl status nginx
# Stop, start, restart
lxc stop myubuntu
lxc start myubuntu
lxc restart myubuntu
# Delete
lxc delete myubuntu
lxc delete myubuntu --force # force-delete a running container
Networking
LXD creates a lxdbr0 bridge by default and gives each container a private IP with NAT to the internet.
# Get a container's IP
lxc list
# Access a service inside the container from the host
lxc exec myubuntu -- ip addr show eth0 # e.g. 10.0.0.123
curl http://10.0.0.123:8080
# Forward a host port to a container port
lxc config device add myubuntu webport proxy \
listen=tcp:0.0.0.0:8080 \
connect=tcp:127.0.0.1:8080
# http://localhost:8080 on host now reaches port 8080 in the container
Persistent Storage
# Mount a host directory into a container
lxc config device add myubuntu mydata disk \
source=/home/$USER/projects \
path=/home/ubuntu/projects
# Share a directory at a simpler path
lxc config device add myubuntu sharedfolder disk \
source=/home/$USER/shared \
path=/shared
Snapshots
LXD supports fast Btrfs-backed snapshots:
# Create a snapshot
lxc snapshot myubuntu snap0
lxc snapshot myubuntu before-update
# List snapshots
lxc info myubuntu | grep -A 10 Snapshots
# Restore
lxc restore myubuntu snap0
# Delete a snapshot
lxc delete myubuntu/snap0
LXD VMs
LXD can also launch full VMs (not just containers) using its --vm flag:
# Launch a VM (hardware-level isolation, own kernel)
lxc launch ubuntu:24.04 myvm --vm
# Manage identically to a container
lxc exec myvm -- bash
lxc stop myvm
Practical Patterns
Running a Web Server in Isolation
lxc launch ubuntu:24.04 webserver
lxc exec webserver -- apt update
lxc exec webserver -- apt install -y nginx
lxc exec webserver -- systemctl enable --now nginx
# Forward port 80 to host port 8080
lxc config device add webserver http proxy \
listen=tcp:0.0.0.0:8080 \
connect=tcp:127.0.0.1:80
curl http://localhost:8080
Testing an Arch Configuration
lxc launch archlinux:current testarch
lxc exec testarch -- bash
# Inside: full Arch Linux with pacman
pacman -Syu && pacman -S some-package
exit
lxc delete testarch --force
Running Multiple Database Versions Side by Side
lxc launch ubuntu:22.04 pg14
lxc exec pg14 -- apt install -y postgresql-14
lxc launch ubuntu:24.04 pg16
lxc exec pg16 -- apt install -y postgresql-16
# Both run simultaneously, fully isolated
Storage
| Location | Subvolume | |---|---| | /var/lib/lxc/ | @lxc | | /var/lib/lxd/ | @lxd |
Both subvolumes are independent of the OS slots and survive all updates and rollbacks.