Install and Use LXC/LXD on VPS — Lightweight Containers

Install and Use LXC/LXD on VPS — Lightweight System Containers

August 2026 · 8 min read

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?

AspectLXDDocker
ConceptFull system container (like a lightweight VM)Application container (1 process)
Init systemFull systemdNone (needs s6/supervisor)
SSH accessYes (ssh or lxc exec)Requires extra setup
NetworkingHas its own IP addressBridge network / port mapping
Best forMulti-service, legacy apps, dev environmentsMicroservices, 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
Tip: Use 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
Tip: For VPS with limited disk space, choose the 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
Tip: If you run web servers in multiple containers, use Nginx or Caddy on the host as a reverse proxy — routing traffic by domain to each container's IP — instead of mapping the same ports repeatedly.

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
Tip: If 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