
September 11, 2026
13 min read
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.
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.04or 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.securebootwhen 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.
| Criteria | LXD system container | Docker application container | Full VM (KVM) |
|---|---|---|---|
| Boot model | Full init/systemd inside guest | Single process or minimal init | Separate guest kernel |
| Density | High; seconds to create | Very high; smallest footprint | Lower; heavier per guest |
| Isolation | Namespace + cgroup boundary | Same kernel primitives | Hardware virtualisation |
| Typical use | Legacy PHP, multi-service stacks | Microservices, CI jobs, stateless APIs | Windows guests, strict isolation |
| Ops style | SSH, apt, cron, journalctl | docker logs, orchestrator specs | Cloud-init, hypervisor tools |
| Snapshot cost | Low with ZFS or BTRFS | Layer-based; depends on storage driver | Often 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.
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
- 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
diron 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.
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.
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
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.

