Virtualization

Run system containers with LXC and LXD on a VPS

If you need multiple isolated Linux environments on a single VPS but don't want the overhead of full virtual machines, LXC and LXD give you a third path, system containers that boot their own init system, get a dedicated network stack, and consume far less memory than KVM or VMware guests. Think of them as lightweight VMs with cgroups-based isolation. With LXD (the modern management layer on top of LXC), provisioning a new container takes seconds, and resource limits are set per-container, not per-VPS. Here is how to set up LXC/LXD on an Ubuntu 24.04 VPS and run system containers in production.

Prerequisites

  • An Ubuntu 24.04 LTS VPS with at least 2 GB RAM and 20 GB of NVMe storage, a 2 GB or 4 GB plan from a Linux VPS host with full root access works well.
  • Root or sudo access.
  • Basic familiarity with the Linux command line and containers.

Why use system containers on a VPS instead of Docker or VMs?

Docker containers share the host kernel and run a single process per container. System containers (LXC/LXD) also share the host kernel but boot a full init system (systemd), so they behave like a separate machine, you can ssh into them, run multiple services, and apply system-wide configs. VMs, by contrast, virtualise the full hardware stack, which wastes resources on a machine that already runs virtualised.

On a VPS, where every vCPU and megabyte of RAM costs you money, switching from KVM guests to LXD containers typically saves 30-40% in memory and avoids the disk I/O overhead of nested block devices. This makes LXD ideal for hosting multiple application stacks, testing environments, or running isolated network services on the same VPS.

Step 1, Install LXC and LXD on Ubuntu 24.04

Both packages are in the official repositories. The recommended LTS track for LXD as of 2026 is 5.21.x. Install LXD (which pulls LXC as a dependency):


sudo apt update
sudo apt install lxd
lxc --version

After installation the lxc command is available. The version output should show something like 5.21.6.

Add your user to the lxd group so you can run LXD commands without sudo:


sudo usermod -aG lxd $USER
newgrp lxd

Verify: Run lxc storage list. It should return an error like Error: not initialized, expected, we will initialise LXD next.

Step 2, Initialise LXD with sensible defaults for a VPS

The lxd init wizard asks about networking, storage, and clustering. For a single VPS, the defaults are fine, but two settings worth tuning:


sudo lxd init

Here is how to answer each prompt for a VPS scenario:

PromptRecommended answer
Would you like to use LXD clustering?no
Do you want to configure a new storage pool?yes
Name of the new storage poolvpspool
Name of the storage backenddir (or btrfs if your VPS supports it; check with lsmod | grep btrfs)
Would you like to connect to a MAAS server?no
Would you like to create a new local network bridge?yes
What should the new bridge be called?lxdbr0
What IPv4 address should be used?10.10.10.1/24
What IPv6 address should be used?none (unless you explicitly need IPv6)
Would you like to configure LXD to use an existing bridge or host interface?no

The wizard completes in a few seconds. LXD now runs its own DNS and DHCP on lxdbr0, so containers get IPs automatically.

Verify: lxc storage list → shows vpspool (or your pool name). lxc network list → shows lxdbr0.

Step 3, Launch your first system container

LXD uses a remote image server. The default is images: (the Linux Containers image server), which distributes official images for many distributions. Launch an Ubuntu 24.04 container:


lxc launch ubuntu:24.04 myfirst

This pulls the image (around 200 MB compressed) and boots the container. The process takes 30-60 seconds depending on your network.

List running containers:


lxc list

Output shows the container name, state, IPv4 address on lxdbr0, and resource usage.

Access the container shell:


lxc shell myfirst

You are now inside a full Ubuntu 24.04 system that shares the host kernel but has its own /etc, /var, init system, and network.

Step 4, Configure resource limits per container

This is where LXD beats raw containers. Set a memory limit before letting a container run unchecked:


lxc config set myfirst limits.memory 512MB
lxc config set myfirst limits.cpu 1
lxc config set myfirst limits.disk.priority 5

Apply limits to a running container, they take effect immediately without a restart. The disk priority value (1-10) controls I/O weights relative to other containers.

Check the applied limits:


lxc config show myfirst

Look for the limits.* entries near the bottom. LXD enforces these with cgroups v2, so even if a process inside the container tries to consume more, it gets throttled at the kernel level.

Step 5, Expose container services to the internet

By default containers get a private IP on lxdbr0 and the host acts as a NAT gateway. To make a service inside the container accessible from outside, use proxy devices. Suppose you run a web server on port 80 inside a container called webcontainer:


lxc config device add webcontainer myproxy proxy \
  listen=tcp:0.0.0.0:8080 connect=tcp:127.0.0.1:80

This tells LXD to listen on the host's IP on port 8080 and forward to the container's loopback on port 80. For a production setup with multiple containers, a reverse proxy (Nginx or Caddy) on the host handles TLS termination and routes traffic per domain, the same architecture you would use with Docker.

Alternative, bridged networking: If you have a second routable IPv4 on your VPS, skip NAT. Attach the container directly to the public bridge:


lxc network attach lxdbr0 webcontainer eth0
lxc config device set webcontainer eth0 ipv4.address <public_ip>

This works only if your VPS provider assigns more than one IP, common with dedicated servers, less so with standard VPS plans.

Step 6, Snapshots, backups, and container migration

LXD supports instant snapshots of a container's filesystem and state:


lxc snapshot myfirst myfirst-snap1
lxc list-snapshots myfirst

Restore from a snapshot:


lxc restore myfirst myfirst-snap1

Export a container to a tarball for offsite backup:


lxc export myfirst /backups/myfirst-20260801.tar.gz

Import it on another LXD host with lxc import /backups/myfirst-20260801.tar.gz.

For regular automated exports, wire a cron job or a systemd timer that runs lxc export with a timestamp. If you run multiple production containers, consider setting up VPS monitoring with Prometheus and Grafana to track resource usage per container.

Troubleshooting

Container fails to start with "untrusted source" error

LXD by default trusts ubuntu: and images: remotes. If you add a custom remote, accept its fingerprint with lxc remote add <name> <url> --protocol=simplestreams.

Cannot reach the internet from inside a container

Check that the host's iptables or nftables rules allow forwarding. LXD automatically adds forwarding rules to lxdbr0, but a firewall on the host may block them. Verify with sysctl net.ipv4.ip_forward, must return 1.

Memory inside the container shows total host memory, not the limit

This is expected, free -h inside the container shows the host total because the kernel exposes the full memory node. The cgroup enforcement happens at the kernel level, not via a synthetic /proc file. Use lxc info myfirst to see the real memory limit and usage from the host side.

FAQ

Is LXD production-ready on a single VPS?

Yes. LXD 5.21 LTS is supported until June 2029 and is used in production at scale by companies like Canonical, OVHcloud, and many hosting providers. For a single VPS, LXD gives you VM-like isolation with container-level overhead.

Does LXD support live migration like KVM?

Live migration requires a shared storage backend (Ceph, ZFS replication) and cluster mode. It works, but on a single VPS without shared storage, you are limited to cold migration via lxc export/import.

Can I run Docker inside an LXD container?

Yes, but with a caveat. Docker inside LXD requires the container to be privileged and have the security.nesting=true property set (lxc config set <container> security.nesting=true). It works for development or lightweight CI runners, but for production, run Docker directly on the host and use LXD for system containers that need a full init environment.

How does LXD compare to Proxmox containers on a VPS?

Proxmox bundles its own LXC stack with a web management UI. LXD is the standalone orchestration layer, no web UI, but easier-to-script CLI and API. Both use the same kernel primitives. On a plain VPS without Proxmox installed, LXD is simpler to set up and consumes fewer resources.

What OS images can LXD run?

Hundreds, including Ubuntu, Debian, Fedora, Alpine, Arch Linux, openSUSE, CentOS (via images:centos/9), and even images:almalinux/9. The images: remote carries community-maintained images for virtually every distribution.

Does LXD support snapshots on a dir-backed storage pool?

Yes, dir backend uses LXD's own snapshot mechanism (copy-on-write via file-tree copies). It is slower than Btrfs/ZFS snapshots but works reliably. For production, consider Btrfs if your kernel supports it.

Related articles

LXC/LXD 系统容器:在VPS上运行轻量级虚拟化

LXC/LXD 允许你在单台 VPS 上运行多个完整的 Linux 系统容器,每个容器拥有独立的 systemd、网络栈和文件系统,内存开销远低于传统 KVM 虚拟机。本文以 Ubuntu 24.04 为例,从安装 LXD、初始化存储池与网桥、启动容器、设定 CPU/内存限制到暴露外部端口,给出了完整的操作步骤。LXD 5.21 LTS 支持至 2029 年,适合在越南 VPS 上托管多套应用环境或隔离服务。结合快照和导出备份,可用于生产部署。

Note: This guide is for general reference. Every system and infrastructure has its own specifics, so test each step in a safe environment and consult a qualified engineer before applying it in production.