LXC and LXD on Shani OS — Full System Containers

LXC and LXD provide full Linux system containers — they run a complete operating system (init system, services, network stack) inside an isolated environment that shares the host kernel. This sits between application containers (Podman/Docker) and full virtual machines (QEMU/KVM) in terms of isolation and overhead.

On Shani OS, LXC and LXD are pre-installed with dedicated Btrfs subvolumes (@lxc and @lxd). lxd.socket is socket-activated. Containers survive every OS update and rollback untouched.

Full reference: docs.shani.dev — Containers.

---

LXC vs LXD vs Distrobox vs Podman vs nspawn vs VMs

Choosing the right container or isolation type:

| | 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 (by default) | No | No | No | No | | Full init system | No | No | Yes | Yes | Yes | | Kernel | Host | Host | Host | Host | Own kernel | | Startup time | ~1s | ~0.1s | ~1s | ~3s | ~30s | | Daemon required | No | No | No | Yes (socket) | Yes | | Isolation | Low–medium | Medium | High | High | Full |

For GUI apps and portable tools, see: Flatpak (guide), Snap (guide), AppImage (guide). For CLI tools and runtimes, see Nix (guide).

Use LXD when:

  • You need a complete isolated server environment (web server, database, multiple services)
  • You want strong isolation but not the overhead of a full VM
  • You are testing system configurations, init systems, or system-level software
  • You want multiple isolated "machines" that boot quickly
  • You need LXD's image catalog, built-in port forwarding devices, or remote container management

Use systemd-nspawn when you want the same full-system isolation as LXD with less setup — no init wizard, no daemon, no image format. Pull a tar and start. See systemd-nspawn on Shani OS.

For the complete picture of all app and container ecosystems on Shani OS — Flatpak, Nix, Distrobox, Podman, Apptainer, VMs, and more — see The Shani OS Software Ecosystem.

---

LXD Setup

lxd.socket is socket-activated on Shani OS. lxcfs.service is also enabled — it provides filesystem virtualisation for containers so that /proc/cpuinfo, /proc/meminfo, and related files report per-container values rather than host totals. Initialize LXD on first use:

# Initialize LXD (interactive wizard)
sudo lxd init

# Or use defaults (good for most cases)
sudo lxd init --auto

The wizard asks about storage backend (use btrfs — it integrates with the host Btrfs), network bridge, and remote access. The Btrfs storage pool maps into the @lxd subvolume.

# Add yourself to the lxd group (Shani OS does this automatically for new users)
groups | grep lxd

# If missing:
sudo usermod -aG lxd $USER
# Log out and back in

---

Creating and Managing Containers with LXD

# 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 running containers
lxc list

# Open a shell in a container
lxc exec myubuntu -- bash
lxc exec myubuntu -- /bin/bash

# Run a specific command
lxc exec myubuntu -- apt update
lxc exec myubuntu -- systemctl status nginx

# Stop and start containers
lxc stop myubuntu
lxc start myubuntu
lxc restart myubuntu

# Delete a container
lxc delete myubuntu
lxc delete myubuntu --force    # force-delete running container

---

Networking in LXD Containers

By default, LXD creates a lxdbr0 bridge and gives each container a private IP address on that bridge. Containers can reach the internet through NAT.

# Get container IP address
lxc list
# Or from inside: ip addr

# Access a web service running in a container from the host
lxc exec myubuntu -- ip addr show eth0   # get container IP, e.g. 10.0.0.123
curl http://10.0.0.123:8080

# Forward a port from host to container
lxc config device add myubuntu webport proxy \
  listen=tcp:0.0.0.0:8080 \
  connect=tcp:127.0.0.1:8080
# Now http://localhost:8080 on the host reaches port 8080 in the container

---

Persistent Storage in Containers

# Mount a host directory into a container
lxc config device add myubuntu mydata disk \
  source=/home/$USER/projects \
  path=/home/ubuntu/projects

# Or share an entire directory
lxc config device add myubuntu sharedfolder disk \
  source=/home/$USER/shared \
  path=/shared

---

LXD Snapshots

LXD supports fast Btrfs-backed container snapshots:

# Create a snapshot
lxc snapshot myubuntu snap0
lxc snapshot myubuntu before-update

# List snapshots
lxc info myubuntu | grep -A 10 Snapshots

# Restore a snapshot
lxc restore myubuntu snap0

# Delete a snapshot
lxc delete myubuntu/snap0

---

Useful LXD 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

# Access from host
curl http://localhost:8080

Testing a New Arch Configuration

lxc launch archlinux:current testarch
lxc exec testarch -- bash

# Inside: full Arch Linux with pacman
pacman -Syu
pacman -S some-package
# Test whatever you need
exit

lxc delete testarch --force

Running Multiple Database Versions

# PostgreSQL 14 in one container
lxc launch ubuntu:22.04 pg14
lxc exec pg14 -- apt install -y postgresql-14

# PostgreSQL 16 in another
lxc launch ubuntu:24.04 pg16
lxc exec pg16 -- apt install -y postgresql-16

# Both run simultaneously, fully isolated

---

Resources

---

Built in India 🇮🇳 · Immutable · Atomic · Zero Telemetry