Skip to content

Linux Containers — LXC & LXD Practical Guide

DodaTech Updated 2026-06-24 9 min read

In this tutorial, you'll learn about Linux Containers. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

LXC (Linux Containers) and LXD (the system container manager) provide operating-system-level virtualization that runs full Linux distributions in isolated environments using cgroups and namespaces, without a hypervisor.

What You'll Learn

How to install and configure LXD, create and manage system containers, apply resource limits, configure networking (bridge, NAT, routed), take snapshots, use profiles for consistent deployments, and integrate with backup workflows.

Why LXC/LXD Matters

Unlike Docker containers (designed for single processes), LXC runs full init systems — you can run systemd, SSH, cron, and multiple processes inside each container. This makes LXC ideal for: replacing lightweight VMs, creating isolated development environments, running legacy applications that need full OS services, and building virtual desktop infrastructure. DodaZIP uses LXD containers for isolated build environments that need systemd access.

Learning Path

flowchart LR
  A[Server Hardening CIS] --> B[LXC Containers
You are here] B --> C[Linux Administration Complete] style B fill:#f90,color:#fff

LXC vs LXD vs Docker

Feature LXC LXD Docker
Scope Low-level API Container manager Application containers
Runs full OS Yes Yes No (single process)
Systemd inside Yes Yes No (requires workarounds)
Snapshot/CLI Manual Built-in Via volumes/images
Live migration No Yes (via CRIU) No
Resource limits Manual Built-in profiles Via Docker Compose
Image format Local templates Remote image servers Docker Hub

Installing LXD

# Install LXD
sudo snap install lxd

# Or via apt (older version)
sudo apt install lxd lxd-client

# Initialize LXD (interactive setup)
sudo lxd init

# Typical init answers:
# 1. Use clustering? No
# 2. Storage pool: dir or zfs (zfs recommended)
# 3. Network bridge: auto (creates lxdbr0)
# 4. IP range: auto (10.x.x.x/24)
# 5. Access: allow non-root users (yes if you want sudo-less access)

Adding User to LXD Group

sudo usermod -aG lxd $USER
newgrp lxd
# Or log out and back in

Container Management

Launching Containers

# List available images
lxc image list images: | grep ubuntu
lxc image list images: | grep alpine

# Launch a container
lxc launch ubuntu:22.04 mycontainer

# Launch with specific alias
lxc launch images:alpine/3.19 alpine-box

# Launch from local image
lxc image copy ubuntu:22.04 local: --alias ubuntu-jammy
lxc launch ubuntu-jammy myapp

Basic Container Commands

# List containers
lxc list

# Container lifecycle
lxc start mycontainer
lxc stop mycontainer
lxc restart mycontainer
lxc pause mycontainer
lxc delete mycontainer

# Execute commands inside
lxc exec mycontainer -- apt update
lxc exec mycontainer -- ls -la /root
lxc exec mycontainer -- bash

# Attach interactive shell
lxc exec mycontainer -- bash

Expected lxc list output:

+-------------+---------+------+------+-----------+-----------+
|    NAME     |  STATE  | IPV4 | IPV6 |   TYPE    | SNAPSHOTS |
+-------------+---------+------+------+-----------+-----------+
| mycontainer | RUNNING | 10.x.x.x |      | CONTAINER | 0         |
| alpine-box  | STOPPED |      |      | CONTAINER | 0         |
+-------------+---------+------+------+-----------+-----------+

Resource Limits with Profiles

Profiles define container configuration and can be applied to multiple containers:

# Create a profile
lxc profile create web-server

# Configure resource limits
lxc profile set web-server limits.cpu 2
lxc profile set web-server limits.memory 512MB
lxc profile set web-server limits.processes 100
lxc profile set web-server security.privileged false
lxc profile set web-server security.nesting false

# Apply to container
lxc profile assign mycontainer web-server

# Launch with profile
lxc launch ubuntu:22.04 webserver --profile web-server

# Device passthrough (disk)
lxc profile device add web-server www-disk disk \
    source=/home/user/www path=/var/www

Resource Profile Examples

# Minimal container profile
lxc profile create minimal
lxc profile set minimal limits.cpu 1
lxc profile set minimal limits.memory 128MB
lxc profile set minimal limits.processes 50

# Resource-heavy container
lxc profile create heavy
lxc profile set heavy limits.cpu 8
lxc profile set heavy limits.memory 16GB
lxc profile set heavy limits.memory.swap false
lxc profile set heavy limits.cpu.allowance 50%   # 50% of 8 CPUs

Storage Pools

# List storage pools
lxc storage list

# Create a ZFS storage pool
lxc storage create pool-zfs zfs source=/dev/sdb

# Create a directory-backed pool
lxc storage create pool-dir dir source=/var/lib/lxd/storage-pools/dir

# Attach to container
lxc storage volume create pool-zfs web-data 10GB
lxc storage volume attach pool-zfs web-data mycontainer /var/www

Snapshots and Backups

# Create snapshot
lxc snapshot mycontainer snap-20260624

# List snapshots
lxc info mycontainer

# Restore snapshot
lxc restore mycontainer snap-20260624

# Export container as tarball (backup)
lxc export mycontainer /backup/mycontainer-$(date +%F).tar.gz

# Import container from backup
lxc import /backup/mycontainer-2026-06-24.tar.gz

# Rename
lxc move mycontainer mycontainer-new

Networking

Default Bridge (lxdbr0)

The default LXD bridge provides NATed networking:

# Show network configuration
lxc network show lxdbr0

# Add a container to the default network
lxc network attach lxdbr0 mycontainer eth0 eth0

Bridged Networking (Same Subnet as Host)

# Create a bridged network interface
sudo ip link add br0 type bridge
sudo ip addr add 192.168.1.100/24 dev br0
sudo ip link set br0 up
sudo ip link set eth0 master br0

# Create LXD managed network
lxc network create br0 --type=bridge \
    parent=br0 \
    ipv4.address=192.168.1.100/24 \
    ipv4.dhcp=true \
    ipv4.nat=false

# Launch container on the bridge
lxc launch ubuntu:22.04 mycontainer --network br0

Proxy Device (Port Forwarding)

# Forward host port 8080 to container port 80
lxc config device add mycontainer web proxy \
    listen=tcp:0.0.0.0:8080 \
    connect=tcp:127.0.0.1:80

Image Management

# Search images
lxc image list images:ubuntu
lxc image list images:alpine

# Copy image to local cache
lxc image copy ubuntu:22.04 local: --alias ubuntu-jammy

# Create custom image from container
lxc publish mycontainer --alias myapp-v1 \
    --description "My Application v1.0"

# List local images
lxc image list

# Delete image
lxc image delete myapp-v1

Building a Custom Image

# 1. Launch base container
lxc launch ubuntu:22.04 build-container

# 2. Configure it
lxc exec build-container -- apt update
lxc exec build-container -- apt install -y nginx nodejs
lxc exec build-container -- systemctl enable nginx

# 3. Publish as image
lxc publish build-container --alias myapp-base --public
lxc delete build-container

# 4. Launch new containers from the image
lxc launch myapp-base app-server-1

Container Configuration

# Show full config
lxc config show mycontainer

# Set environment variable
lxc config set mycontainer environment.NODE_ENV production

# Set boot options
lxc config set mycontainer boot.autostart true
lxc config set mycontainer boot.autostart.delay 5
lxc config set mycontainer boot.autostart.priority 10

# Show resource usage
lxc info mycontainer --resources

Common Errors

1. Container Fails to Start with "No such file or directory"

The init system binary is missing or the image is incomplete. Use a standard image: lxc launch ubuntu:22.04 test. Avoid custom minimal images without init.

2. "Permission denied" When Using LXC Commands

The user is not in the lxd group. Add them with sudo usermod -aG lxd $USER and log out/in.

3. Network Not Working Inside Container

The LXD bridge (lxdbr0) is not configured or DHCP is disabled. Run lxc network show lxdbr0 to verify. Re-init: sudo lxd init and accept the default bridge.

4. Container Out of Disk Space

LXD thin-provisioned storage can run out of pool space. Check lxc storage list and lxc storage info pool-zfs. Expand the pool or add storage.

5. systemd Services Fail Inside Container

By default, LXD containers run with security.privileged=false and systemd is confined by AppArmor. Use lxc config set container security.privileged true only if necessary.

6. Snapshot Restore Fails

Snapshots cannot be restored if newer snapshots exist (LXD does not allow non-linear restore). Delete newer snapshots first, or export/import.

7. Container IP Changes After Reboot

LXD uses DHCP by default. Set a static IP with lxc config device override mycontainer eth0 ipv4.address=10.10.10.100.

Practice Questions

1. What is the difference between LXC and LXD? LXC is the low-level library and tools for creating containers. LXD is a management daemon that provides a REST API, image management, snapshots, live migration, and a seamless user experience on top of LXC.

2. How do you limit a container to 1GB of memory? lxc config set mycontainer limits.memory 1GB or use a profile: lxc profile set myprofile limits.memory 1GB.

3. How do you create a snapshot of a running container? lxc snapshot mycontainer snap-name. Snapshots are instantaneous with copy-on-write storage.

4. How do you forward host port 80 to a container's port 8080? Add a proxy device: lxc config device add mycontainer http proxy listen=tcp:0.0.0.0:80 connect=tcp:127.0.0.1:8080.

5. What command creates a reusable image from a configured container? lxc publish mycontainer --alias my-image. The container is published as a local image that can launch new containers.

Challenge: Set up a three-container web application stack: (1) an Nginx reverse proxy with SSL termination, (2) a Node.js application server, (3) a PostgreSQL database. Each container should have appropriate resource limits. The proxy container exposes ports 80 and 443 to the host. Configure LXD networking so containers communicate via a private bridge but the proxy is publicly reachable. Take a full snapshot and verify restore.

Is LXD production-ready for running multiple applications on a single host?

Yes — LXD is used by Ubuntu Pro, CNCF, and enterprise deployments. It handles thousands of containers per host with minimal overhead.

Can I migrate a running LXD container to another host?

Yes — LXD supports live migration using CRIU (Checkpoint/Restore In Userspace). Both hosts must have matching kernel versions and LXD configurations.

How is LXD different from Docker Compose?

Docker runs one process per container. LXD runs a full init system. Use Docker for Microservices, LXD for OS-level isolation (dev VMs, legacy apps, multi-service environments).

Does LXD support GPU passthrough?

Yes — lxc config device add mycontainer gpu gpu passes through GPUs. NVIDIA GPU support requires the nvidia-container-runtime.

How do I monitor LXD container resource usage?

lxc info mycontainer --resources shows CPU, memory, and disk usage. For real-time metrics, use lxc top or integrate with Prometheus via the LXD metrics endpoint.

What's Next

Linux Administration — Full Course
Server Hardening — CIS Benchmarks
journalctl — Querying Systemd Logs

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. Updated 2026-06-24.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro