Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

LXC and LXD: System Containers

By Kokil Thapa | Last reviewed: September 2026

LXC and LXD: system containers sit between bare metal and full virtual machines. You get a complete Linux guest—init, systemd, SSH, package manager—without booting a separate kernel. On real client projects I run multiple isolated stacks on one Ubuntu server: a Laravel booking app, a WordPress site, and a Redis cache node, each in its own container. That pattern maps cleanly to how many Nepal agencies host several client sites on shared VPS hardware. If you already manage Linux system administration for production apps, LXD is worth learning before you reach for heavier VM tooling.

What are LXC and LXD system containers?

Linux Containers (LXC) are OS-level virtualization built from kernel features: namespaces, cgroups, and optional AppArmor or SELinux profiles. A system container boots a normal userspace. You log in with SSH. You install packages with apt or dnf. Processes behave as they would on a dedicated VPS.

LXD is a management layer on top of LXC. Canonical maintains it. The lxd daemon handles image servers, profiles, storage backends, bridged networking, snapshots, live migration on supported setups, and a REST API. You interact through the lxc CLI. Under the hood it still creates LXC containers.

Think of the split this way: LXC is the low-level runtime. LXD is the control plane. Docker also uses namespaces and cgroups, but its default workflow packages one application per container. LXD targets operators who want a miniature server, not a single binary in a stripped image.

Ubuntu Host — Single Linux KernelLXD Container Asystemd + NginxPHP-FPM 8.5MySQL clientLXD Container BWordPress 7.1WooCommerce 11.1MariaDB 12.3LXD Container CRedis 8.10Queue workersCron jobsShared Kernel Resourcesnamespaces · cgroups · network bridge · ZFS or dir storageLXD daemon manages profiles, snapshots, and API access
LXC and LXD system containers share one host kernel while each guest runs a full Linux userspace stack.

Kernel namespaces isolate process trees, mount points, network interfaces, users, and IPC. Cgroups cap CPU, memory, and I/O. Together they create boundaries strong enough for multi-tenant hosting. They are not hardware virtualisation. A kernel exploit that escapes namespaces affects the host. Plan patching and privilege boundaries accordingly.

For background on how runtimes plug into orchestrators, read the companion piece on the Container Runtime Interface. LXD is often operated directly on a single host or small cluster rather than through Kubernetes, which suits many agency and SMB workloads.

Core components you will touch daily

  • Images — Pre-built root filesystems from images:ubuntu/24.04 or custom golden images.
  • Profiles — Reusable config bundles for CPU limits, cloud-init snippets, and network attachment.
  • Storage pools — Directory, ZFS, BTRFS, or LVM backends; ZFS gives cheap snapshots.
  • Networks — Bridged lxdbr0, macvlan, or routed setups for public IPs.
  • Instances — Containers (default) or true VMs via security.secureboot when you need a separate kernel.

How do LXC and LXD differ from Docker application containers?

Docker (and containerd) optimises for immutable application images. You build from a Dockerfile. You ship layers. You often run one main process. Logs go to stdout. Config arrives through env vars and secrets mounts.

LXD optimises for machine-like workloads. You launch an Ubuntu guest. You enable systemd. You install PHP, Nginx, and MySQL the same way you would on a VPS. That matches how I deploy Laravel 12 and Symfony 8.1 apps when the client expects a familiar SSH-and-apt workflow rather than a twelve-service Compose file.

CriteriaLXD system containerDocker application containerFull VM (KVM)
Boot modelFull init/systemd inside guestSingle process or minimal initSeparate guest kernel
DensityHigh; seconds to createVery high; smallest footprintLower; heavier per guest
IsolationNamespace + cgroup boundarySame kernel primitivesHardware virtualisation
Typical useLegacy PHP, multi-service stacksMicroservices, CI jobs, stateless APIsWindows guests, strict isolation
Ops styleSSH, apt, cron, journalctldocker logs, orchestrator specsCloud-init, hypervisor tools
Snapshot costLow with ZFS or BTRFSLayer-based; depends on storage driverOften heavier disk use

Neither replaces the other. I run Docker Compose for local Laravel development and LXD on a dedicated host when three clients need isolated stacks but cannot justify three separate cloud VMs. Cost in Kathmandu often lands around Rs 8,000–15,000/month (~USD 60–110) for a mid-size VPS; LXD stretches that budget.

Resource limits work differently too. Docker makes cgroup caps explicit in Compose or Kubernetes. LXD exposes the same through profiles. See limiting Docker container resources for the parallel Docker knobs; the cgroup v2 concepts transfer directly.

Choose Your Isolation ModelWhat are you running?One app per imageUse Docker / ComposeFull Linux serverUse LXD system containerSeparate kernel reqUse KVM / cloud VMMixed production hostLXD guests + Docker CI jobs
Decision guide for LXC and LXD system containers versus Docker application containers and full VMs.

How do you install and configure LXD on Ubuntu?

Ubuntu 22.04 and 24.04 ship LXD as a snap package. Installation takes minutes on a fresh VPS. I use this stack on the same EC2 hosts that run Deployer 7 releases for legal-tech sister sites—see the Notary Kathmandu portfolio entry for the public-facing result of that pipeline.

Install LXD and run the initializer

  1. Update the host and install the snap:
sudo apt update && sudo apt install -y snapd
sudo snap install lxd
sudo lxd init

The interactive wizard asks about clustering, storage pool type, network bridge, and whether to expose the API over the network. For a single production host I accept these defaults unless disk layout demands ZFS:

  • Storage pool: ZFS if the VPS has a spare block device; otherwise dir on a fast SSD path.
  • IPv6: disable if your provider charges for IPv6 routing you do not use.
  • MAAS integration: skip unless you operate bare metal at scale.

Create your first system container

lxc launch images:ubuntu/24.04 app-one -c limits.cpu=2 -c limits.memory=2GB
lxc exec app-one -- bash
apt update && apt install -y nginx php8.3-fpm php8.3-mysql mysql-client redis-tools

That gives you a working guest in under a minute. Assign a static IP on the bridge or attach a routed public address from your provider. List instances with lxc list. Snapshot before major upgrades with lxc snapshot app-one pre-upgrade.

Official reference material lives in the Ubuntu LXD documentation. For the lower-level API that LXD wraps, the Linux Containers project site still documents LXC config keys.

Profiles for repeatable limits

lxc profile create web-small
lxc profile set web-small limits.cpu 2
lxc profile set web-small limits.memory 2GB
lxc profile set web-small limits.processes 500
lxc launch images:ubuntu/24.04 shop -p web-small -p default

Profiles keep staging and production consistent. Pair them with cloud-init user data for SSH keys and package lists. That beats hand-configuring every guest.

LXD Container Lifecyclelxc launchLXD daemonvalidates profileStorage poolRunning guestOperator actions after bootlxc exec · lxc snapshot · lxc copy · lxc move · lxc restartProfiles apply CPU, memory, and network at create timeZFS snapshots roll back botched apt upgrades in seconds
How the LXD daemon turns a launch command into a running LXC and LXD system container with pooled storage.

How do you deploy a Laravel or PHP stack inside LXD?

Most of my production Laravel 12 deployments still use Nginx, PHP-FPM, Redis, and MySQL or PostgreSQL 18 on a single logical host. LXD lets you split those roles across guests without multiplying cloud invoices. The pattern below mirrors what I use before enterprise application development engagements move to dedicated hardware.

Split web and database tiers

lxc launch images:ubuntu/24.04 laravel-web -p web-small
lxc launch images:ubuntu/24.04 laravel-db -c limits.memory=4GB

lxc exec laravel-db -- bash -c "apt update && apt install -y mysql-server"
lxc exec laravel-web -- bash -c "apt update && apt install -y nginx php8.3-fpm php8.3-mysql php8.3-redis redis-server"

Containers resolve each other by name on lxdbr0. Point Laravel's .env at the database container IP or a static lease. Run queue workers under systemd inside the web guest—the same unit files you would use on a bare VPS.

For Git-based deploys I either mount a host path into the container or pull releases over SSH from the host runner. Several sister sites share a Deployer 7 plus GitLab CI pipeline; the release artifact lands on the host, then syncs into the guest document root. That avoids running Node.js 26 LTS on production when Vite 8.x builds happen in CI.

Reverse proxy on the host

Often one Nginx instance on the host terminates TLS with Let's Encrypt. It forwards to container private IPs. This keeps certificate management central while apps stay isolated.

server {
    listen 443 ssl http2;
    server_name app.example.com;
    location / {
        proxy_pass http://10.02.001.45:80;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Validate JSON API responses during integration work with the site JSON formatter tool—handy when proxy headers change client IP behaviour.

WordPress 7.1 with WooCommerce 11.1 fits the same model. I have shipped florist eCommerce on WooCommerce where the database lived in a separate guest to keep backup windows short. See the Petals Agro Nepal portfolio case for the business context behind that architecture choice.

Production LXD LayoutUbuntu HostNginx TLS · LXD bridge · CertbotWeb containerLaravel 13 · PHP-FPM 8.5Redis queue · HorizonDeployer release pathPrivate IP on lxdbr0DB containerMySQL 9.7 or PG 18Nightly mysqldump cronSnapshot before migrationsNot exposed publicly
Typical LXC and LXD system containers layout for a Laravel production stack behind host-level TLS termination.

How do you secure and operate LXD containers in production?

System containers are only as safe as kernel isolation and your ops discipline. Treat them like small VPS instances on shared iron. Patch the host kernel first. Then patch guests. Stagger reboots during maintenance windows.

Unprivileged containers and UID maps

Default LXD containers on recent Ubuntu are unprivileged. Root inside the guest maps to a high UID on the host. A container breakout does not immediately grant host root. Verify with:

lxc config show app-one | grep privileged
lxc info app-one | grep "Security model"

For stricter separation consider rootless container patterns in Docker workloads running beside LXD on the same host. Keep CI build containers under a separate user namespace where possible.

Network exposure and firewall rules

Do not publish database ports to the public Internet. Bind MySQL to the bridge IP. Use UFW on the host to allow 443 and SSH only. Internal traffic stays on lxdbr0. Document which guest owns which port in your runbook—future you will thank present you during incident response.

Backups, monitoring, and capacity

ZFS snapshots are fast. They are not off-site backups. Export nightly with lxc export or dump databases from inside the guest. Monitor host CPU steal, disk latency, and guest memory pressure. When guests compete for I/O, revisit capacity planning for growing systems before vertical scaling becomes mandatory.

Scan images if you build custom ones. Application containers get more attention in Trivy container scanning guides; the same scanner works on exported LXD rootfs tarballs. Store golden images in a private registry workflow described in Harbor private registry setup if you standardise builds across hosts.

Common production mistakes

  • Running every guest as a privileged container to avoid permission friction.
  • Skipping host kernel updates because guests still boot.
  • Oversubscribing RAM because LXD guests look lighter than KVM VMs.
  • Mixing production and experimental guests on one bridge without labels.
  • Forgetting that cron inside a guest uses the guest timezone—not the host.

Ongoing hosting and patch cycles belong in a support and maintenance plan. LXD reduces VM count; it does not remove operational responsibility.

Key Takeaways

  • LXC and LXD system containers deliver full Linux guests on a shared kernel—ideal when you need systemd, SSH, and apt inside each environment.
  • Use LXD profiles and storage pools to standardise CPU, memory, and snapshot policy across staging and production guests.
  • Pair host-level Nginx TLS with private bridge networking; never expose database containers directly to the public Internet.
  • Prefer unprivileged containers, keep the host kernel patched, and treat snapshots as rollback tools—not your only backup.
  • Combine LXD for long-lived stacks with Docker for CI and ephemeral jobs when one VPS serves multiple clients.
  • Document IP layout, deploy paths, and backup ownership before you add a fourth guest—you will outgrow mental notes quickly.

People Also Ask

Can LXD run Windows or macOS guests?

LXD focuses on Linux system containers and Linux VMs via QEMU when you enable the VM feature. Windows guests belong on KVM, VMware, or your cloud provider's hypervisor. For mixed fleets, keep Windows workloads on dedicated virtualisation and use LXD for Linux density on Ubuntu hosts.

Is LXD the same as LXC?

LXC is the userspace tooling and liblxc library that creates containers through kernel namespaces. LXD is a higher-level daemon that manages many LXC instances, storage, networks, and clustering. You can use raw LXC without LXD, but most Ubuntu operators choose LXD for the API and operational ergonomics.

Does Laravel or PHP run better in LXD than Docker?

Performance is similar when CPU and memory limits match—the same kernel runs both. Choose LXD when your team expects traditional server administration: PHP-FPM pools, systemd units, and apt-managed extensions. Choose Docker when you want immutable images, horizontal scaling, and orchestrator integration. Many teams use both on one host.

How does LXD networking work with public IP addresses?

Default installs create a private NAT bridge. Containers reach the Internet through the host. For public IPs you can attach macvlan or routed networks, or proxy from the host as shown above. Cloud providers that charge per IP often favour one host IP plus reverse proxy rather than one public IP per guest.

Ship isolated stacks without multiplying VM bills

LXC and LXD system containers give developers and agencies a practical middle ground: stronger isolation than bare processes, less overhead than full virtual machines, and an operations model that fits Laravel, WordPress, and legacy PHP stacks you already know how to tune. Start with one non-production guest, snapshot before every change, and document your bridge layout before onboarding client number three.

If you want help designing host layout, deploy pipelines, or migration from shared hosting to containerised Ubuntu servers, review domain registration and hosting options or web development services, then contact us to plan a production-ready setup. For related reading, compare runtime layers in Docker Compose multi-container local development and container registry options, or learn more about my infrastructure work on client platforms like Adventure Third Pole Trek.

Frequently Asked Questions

OS-level Linux guests on a shared host kernel using namespaces and cgroups. Each runs full userspace—init, systemd, SSH, apt—like a small VPS, not a single-process Docker image.

Around Rs 8,000–15,000/month (~USD 60–110) for a mid-size VPS in Kathmandu. LXD lets you run several isolated stacks on one bill instead of paying for separate VMs.

No. LXC is the low-level runtime and liblxc library that creates containers through kernel namespaces. LXD is a higher-level daemon Canonical maintains—it manages many LXC instances plus storage pools, networks, profiles, snapshots, and a REST API. Most Ubuntu operators use LXD for operational ergonomics rather than raw LXC config files alone.

Docker optimises for immutable application images, often one main process, logs to stdout, and config via env vars. LXD optimises for machine-like workloads: you launch Ubuntu, enable systemd, install Nginx and PHP-FPM with apt—the same SSH-and-package-manager workflow as a VPS. Density is high for both, but LXD fits legacy PHP, multi-service Laravel stacks, and WordPress when clients expect familiar server administration rather than a twelve-service Compose file.

On Ubuntu 22.04 or 24.04, install via snap: update the host, install snapd, run sudo snap install lxd, then sudo lxd init. The wizard asks about clustering, storage pool type, network bridge, and API exposure. For a single production host, accept defaults unless disk layout demands ZFS: use ZFS on a spare block device, otherwise dir on a fast SSD path, disable IPv6 if your provider charges for unused routing, and skip MAAS unless you operate bare metal at scale.

LXD supports directory, ZFS, BTRFS, and LVM backends. ZFS is the practical choice when you have a spare block device—it gives cheap, fast snapshots useful before major upgrades. Without dedicated disk, use dir on a fast SSD path. ZFS snapshots are excellent rollback tools, but they are not off-site backups; export nightly with lxc export or dump databases from inside guests for real disaster recovery.

No. LXD focuses on Linux system containers and Linux VMs via QEMU when you enable the VM feature. Windows and macOS belong on KVM, VMware, or your cloud provider's hypervisor.

Performance is similar when CPU and memory limits match—the same kernel runs both. Choose LXD when your team expects traditional server administration: PHP-FPM pools, systemd queue workers, and apt-managed extensions. Choose Docker for immutable images, horizontal scaling, and orchestrator integration. Many teams run Docker Compose locally and LXD on a dedicated host when several clients need isolated stacks without separate cloud VMs.

Split tiers across guests: launch a web container with Nginx, PHP 8.3-FPM, and Redis, and a database container with MySQL or PostgreSQL 18. Containers resolve each other by name on lxdbr0—point Laravel's .env at the database container IP. Run queue workers under systemd inside the web guest. For Git-based deploys, mount a host path or sync Deployer 7 release artifacts from CI into the guest document root, keeping Node.js 26 LTS and Vite 8.x builds in GitLab CI rather than on production.

Default installs create a private NAT bridge (lxdbr0). Containers reach the Internet through the host. For public-facing apps, common patterns are macvlan or routed networks for direct attachment, or—often cheaper when providers charge per IP—one host public IP with Nginx terminating TLS via Let's Encrypt and proxy_pass to container private addresses. Keep database ports bound to bridge IPs only, never exposed to the public Internet.

Patch the host kernel first, then patch guests, staggering reboots during maintenance windows. Default LXD containers on recent Ubuntu are unprivileged—root inside maps to a high UID on the host, limiting breakout impact. Verify with lxc config show and lxc info. Use UFW on the host allowing 443 and SSH only. Do not publish database ports publicly. Document which guest owns which port in your runbook. Scan custom golden images and store them in a private registry workflow if you standardise builds across hosts.

No—they share the host kernel, so they are namespace and cgroup isolation, not hardware virtualisation. A kernel exploit that escapes namespaces affects the host. VMs with separate guest kernels provide stronger boundaries for strict isolation needs. LXD also supports true VMs via security.secureboot when you need a separate kernel. For multi-tenant agency hosting on Ubuntu, unprivileged containers plus disciplined patching and network segmentation are usually sufficient—but plan privilege boundaries accordingly.

Profiles are reusable config bundles for CPU limits, memory caps, process counts, cloud-init snippets, and network attachment. Create one like web-small with limits.cpu 2, limits.memory 2GB, and limits.processes 500, then launch instances with lxc launch images:ubuntu/24.04 shop -p web-small -p default. Profiles keep staging and production consistent and beat hand-configuring every guest. Pair them with cloud-init user data for SSH keys and package lists on first boot.

Choose LXD when you need stronger isolation than bare processes but less overhead than full VMs—seconds to create guests, high density, low snapshot cost with ZFS. It suits Laravel, WordPress, and legacy PHP stacks where operators want systemd, SSH, and apt inside each environment. Choose full KVM VMs when you need a separate guest kernel, Windows workloads, or strict hardware-level isolation. LXD reduces VM count on a mid-size VPS; it does not remove operational responsibility.

Running every guest as a privileged container to avoid permission friction. Skipping host kernel updates because guests still boot. Oversubscribing RAM because LXD guests look lighter than KVM VMs. Mixing production and experimental guests on one bridge without labels. Forgetting cron inside a guest uses the guest timezone, not the host. Treating ZFS snapshots as your only backup. Exposing database containers directly to the public Internet. Document IP layout, deploy paths, and backup ownership before adding a fourth guest—you will outgrow mental notes quickly.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: