Install and Use LXC/LXD on VPS — Lightweight System Containers
LXC (Linux Containers) and LXD (Linux Daemon) are OS-level container technologies that provide a VM-like experience while consuming far fewer resources. They are ideal for running multiple isolated environments on a single VPS — such as dev/staging/production or separate project environments. LXD wraps LXC with a REST API and easy-to-use CLI. Unlike Docker, which focuses on immutable single-process application containers, LXC/LXD provides full system containers with a complete init process, systemd, and a full OS — essentially a lightweight VPS inside your VPS.
LXD vs Docker — Which Should You Use?
| Aspect | LXD | Docker |
|---|---|---|
| Concept | Full system container (like a lightweight VM) | Application container (1 process) |
| Init system | Full systemd | None (needs s6/supervisor) |
| SSH access | Yes (ssh or lxc exec) | Requires extra setup |
| Networking | Has its own IP address | Bridge network / port mapping |
| Best for | Multi-service, legacy apps, dev environments | Microservices, CI/CD, packaging |
Install LXD on Ubuntu VPS
LXD is available on Ubuntu 22.04/24.04 via snap:
# Update package list
sudo apt update && sudo apt upgrade -y
# Install LXD via snap (latest stable)
sudo snap install lxd
# Or install an LTS version
sudo snap install lxd --channel=5.21/stable
# Verify installation
lxd --version
lxc --version
snap install lxd --channel=5.21/stable for production stability. The latest/stable channel auto-upgrades when new versions are released.
Add Your User to the lxd Group
# Add the current user to the lxd group
sudo usermod -aG lxd $USER
# Reload group membership without logging out
newgrp lxd
# Verify group membership
groups $USER
Initial LXD Configuration (lxd init)
Run lxd init to configure LXD — using defaults is sufficient for most VPS setups:
# Auto-configure with defaults
lxd init --auto
# Or run interactive setup (recommended for production)
lxd init
The interactive setup asks these questions:
Would you like to use LXD clustering? (yes/no) [default=no]: no
Do you want to configure a new storage pool? (yes/no) [default=yes]: yes
Name of the new storage pool [default=default]: default
Name of the storage backend to use (btrfs, dir, lvm, zfs, ceph) [default=zfs]: dir
Would you like to connect to a MAAS server? (yes/no) [default=no]: no
Would you like to create a new local network bridge? (yes/no) [default=yes]: yes
What should the new bridge be called? [default=lxdbr0]: lxdbr0
What IPv4 address should be used? (CIDR subnet notation, "auto" or "none") [default=auto]: auto
Would you like the LXD server to be available over the network? (yes/no) [default=no]: no
Would you like stale cached images to be updated automatically? (yes/no) [default=yes]: yes
Would you like a YAML "lxd init" preseed to be printed? (yes/no) [default=no]: no
dir backend instead of zfs — it doesn't require a separate partition. Use zfs when you have several GB of free disk space and want snapshot support.
Create Your First Container
# Browse available images (Ubuntu, Debian, Alpine, CentOS, Arch, etc.)
lxc image list images: | grep -E "ubuntu|debian" | grep amd64 | head -10
# Launch a container from Ubuntu 24.04
lxc launch ubuntu:24.04 mycontainer
# Launch a container from Debian 12
lxc launch images:debian/12 mydebian
# List all containers
lxc list
# Show container details
lxc info mycontainer
Access a Container
# Open a shell inside the container
lxc exec mycontainer -- bash
# Run a single command inside the container
lxc exec mycontainer -- apt update
# Access as a specific user
lxc exec mycontainer -- su - ubuntu
# View container logs
lxc console mycontainer --show-log
Managing Containers — Start, Stop, Delete
# Stop a container
lxc stop mycontainer
# Start a container
lxc start mycontainer
# Restart a container
lxc restart mycontainer
# Delete a container (must stop first)
lxc stop mycontainer
lxc delete mycontainer
# Force delete (no need to stop first)
lxc delete mycontainer --force
Set Resource Limits (CPU/RAM)
Restrict container resources to prevent any single container from consuming all host resources:
# Limit RAM to 512MB
lxc config set mycontainer limits.memory 512MB
# Limit to 1 CPU core
lxc config set mycontainer limits.cpu 1
# View current config
lxc config show mycontainer
# Set limits at launch time
lxc launch ubuntu:24.04 webserver \
-c limits.memory=1GB \
-c limits.cpu=2
Port Forwarding — Expose Services to the Outside
Each container has an internal IP (172.x.x.x). Use proxy devices to make services accessible from outside:
# Check the container's IP
lxc list
# Forward host port 80 → container port 80
lxc config device add mycontainer http proxy \
listen=tcp:0.0.0.0:80 \
connect=tcp:127.0.0.1:80
# Forward host port 443 → container port 443
lxc config device add mycontainer https proxy \
listen=tcp:0.0.0.0:443 \
connect=tcp:127.0.0.1:443
# List attached devices
lxc config device list mycontainer
# Remove a proxy device
lxc config device remove mycontainer http
Snapshots — Save Container State
# Create a snapshot before major changes or upgrades
lxc snapshot mycontainer snap0
# List snapshots
lxc info mycontainer | grep Snapshots -A 20
# Restore a snapshot
lxc restore mycontainer snap0
# Delete a snapshot
lxc delete mycontainer/snap0
Mount a Host Directory into a Container
# Mount /home/ubuntu/data from the host into /data inside the container
lxc config device add mycontainer datadir disk \
source=/home/ubuntu/data \
path=/data
# Verify the mount
lxc exec mycontainer -- ls /data
# Remove the device
lxc config device remove mycontainer datadir
Verify LXD is Working Correctly
# Check LXD service status
sudo systemctl status snap.lxd.daemon.service
# List all containers with their IPs
lxc list
# Check the LXD network bridge
ip addr show lxdbr0
# Check storage pool
lxc storage list
lxc storage info default
# Ping a container from the host
lxc list # find the IP first
ping -c 3 10.x.x.x
lxc list shows a container as RUNNING but with no IP, check whether lxd-agent is running inside the container: lxc exec <name> -- systemctl status lxd-agent
Troubleshooting Common Errors
Error: "dial unix /var/snap/lxd/common/lxd/unix.socket: connect: permission denied"
Your user is not in the lxd group yet, or you need to log out and back in after adding the group membership.
# Check if you're in the lxd group
groups $USER
# Add yourself if missing
sudo usermod -aG lxd $USER
# Reload group membership without logout
newgrp lxd
Error: "Failed to create container: The container already exists"
A container with that name already exists. Delete it or use a different name.
# List all containers
lxc list
# Delete the old container
lxc delete oldcontainer --force
# Or use a new name
lxc launch ubuntu:24.04 mycontainer2
Error: "Failed to pull image: context deadline exceeded"
Network timeout while downloading the image — likely caused by DNS issues or VPS firewall rules.
# Test DNS resolution
nslookup cloud-images.ubuntu.com
# Try pulling the image again
lxc image copy ubuntu:24.04 local: --alias ubuntu-24
# Use the cached local image
lxc image list
lxc launch local:ubuntu-24 mycontainer
Error: "Container has no valid IP address" after launch
The lxdbr0 bridge may have an issue, or the DHCP client inside the container isn't running.
# Check the bridge
ip link show lxdbr0
# Restart networking inside the container
lxc exec mycontainer -- systemctl restart systemd-networkd
# Restart the container
lxc restart mycontainer
# Restart the LXD daemon
sudo systemctl restart snap.lxd.daemon.service
Error: proxy device fails to apply
The port you're trying to forward may already be in use on the host, or the container may not be running.
# Check if the port is already in use
sudo ss -tuln | grep :80
# Start the container first, then add the proxy
lxc start mycontainer
lxc config device add mycontainer http proxy listen=tcp:0.0.0.0:80 connect=tcp:127.0.0.1:80
Frequently Asked Questions
How is LXD different from Docker, and which should I use?
LXD is a system container that emulates a full OS — with systemd, an init process, and SSH — similar to a VM but much lighter. It is best suited for running multiple services within a single environment, testing legacy software, or isolating dev/production environments on one VPS. Docker is better for application packaging, microservices, and CI/CD pipelines that require immutable containers. In practice, you can run both on the same VPS — LXD for environment isolation, Docker inside each LXD container for application deployment.
How much RAM does an LXD container use compared to a VM?
An idle LXD container typically uses just 20–50 MB of RAM, compared to hundreds of MB for a KVM VM. Because LXD containers share the host kernel rather than booting a separate OS, a 2GB VPS can comfortably run 10–20 LXD containers simultaneously — depending on the services running inside each one.
Can I run Docker inside an LXD container?
Yes, but you need to enable nested containers first by setting security.nesting=true on the container.
# Enable nested containers
lxc config set mycontainer security.nesting true
# Restart the container
lxc restart mycontainer
# Install Docker inside
lxc exec mycontainer -- bash
# Then run: curl -fsSL https://get.docker.com | sh