LXC containers (Linux Containers) in Proxmox share the host's kernel, which makes them far lighter and faster than full VMs. A container starts in seconds and consumes minimal resources while offering the same level of isolation for most tasks that do not need a separate kernel or a different operating system.
VM vs LXC: when to use which
If the task calls for a full VM, the creation steps are covered in creating a virtual machine in Proxmox VE.
| Criterion | VM (KVM) | LXC container |
|---|---|---|
| Startup time | 30-60 seconds | 1-3 seconds |
| RAM consumption | Higher (its own kernel) | Lower (shared kernel) |
| Isolation | Full (hardware) | Partial (namespaces) |
| OS | Any (Windows, Linux, BSD) | Linux only |
| I/O performance | Small overhead | Near-native |
| Docker support | Full | Limited (needs nesting) |
Step 1: download a container template
# Refresh the template list
pveam update
# Show the available templates
pveam available | grep -E "debian|ubuntu|alpine"
# Download a template
pveam download local debian-12-standard_12.7-1_amd64.tar.zst
Or through the web interface: local → CT Templates → Templates, then pick one and download it.
Step 2: create the container
# Create an unprivileged container with ID 200
pct create 200 local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst \
--hostname mycontainer \
--rootfs local-lvm:8 \
--memory 512 --swap 512 --cores 1 \
--net0 name=eth0,bridge=vmbr0,ip=dhcp \
--password SecurePass123 \
--unprivileged 1 --onboot 1
# Start the container
pct start 200
# Open the console
pct enter 200
Common errors explained
- unable to create CT 200 - unable to parse value of 'rootfs' - storage 'local-lvm' does not exist — the host uses a different storage backend (ZFS, a directory, and so on); use the real name from
pvesm statusinstead of a value copied from an unrelated example. - startup for container '200' failed in the log after
pct start— usually an IP conflict or thevmbr0bridge not being up on the host yet; checkip aand confirm the bridge exists before creating the container. - lxc-start: 200: ... Permission denied - Failed to mount — an attempt to mount a device or filesystem inside an unprivileged container that requires root on the host; either lift the restriction for that specific case or switch to a privileged container just for that task.
- CT is locked (snapshot) when trying to change the configuration — a previous snapshot operation did not finish; the lock can be cleared with
pct unlock 200, but only after confirming the operation is truly stuck.
Privileged vs unprivileged containers
Recommendation: always use --unprivileged 1 (the default). Unprivileged containers are safer — processes inside run as UID/GID shifted by 100000, which rules out privilege escalation to the host even if a process inside the container is compromised.
Privileged containers are only needed for specific tasks: working with devices, NFS mounts, and a few networking features that require real root on the host.
Setting up bind mounts
# Mount a host directory into the container
pct set 200 --mp0 /data/shared,mp=/mnt/shared,backup=0
# Mount it through the config file
cat /etc/pve/lxc/200.conf
# mp0: /data/shared,mp=/mnt/shared
Enabling Docker inside LXC
Running Docker inside an LXC container needs extra settings:
# Add to /etc/pve/lxc/200.conf on the Proxmox host
features: keyctl=1,nesting=1
# Restart the container
pct stop 200 && pct start 200
# Install Docker inside the container
curl -fsSL https://get.docker.com | bash
Security: Docker in LXC with nesting=1 reduces isolation. For production workloads, run Docker inside a full VM instead.
Comparing storage options for container rootfs
Details on each storage type are in the article on storage in Proxmox VE.
| Storage | Snapshots | Speed | When to use it |
|---|---|---|---|
| local-lvm (thin) | Yes | High | Default for most nodes |
| ZFS | Yes, instant | High | Frequent snapshots and replication needed |
| Directory | No | Medium | Test environments, simple setup |
When LXC does not fit
- A non-Linux system is needed (Windows, FreeBSD) — LXC shares the kernel with the host and physically cannot run a different OS; that calls for a full VM on KVM.
- The application needs its own network stack with kernel parameter changes (a custom sysctl set for routing, for example) — the host's shared kernel limits such settings at the container level.
- Guaranteed hard isolation is required for a multi-tenant environment with untrusted users — LXC namespaces have historically had more escape vulnerabilities than KVM's hardware virtualization.
Useful pct commands
# List containers
pct list
# Container status
pct status 200
# Stop it
pct stop 200
# Snapshot
pct snapshot 200 backup1
# Clone the container
pct clone 200 201 --full --hostname container2
# Delete it
pct destroy 200
How to verify the container works correctly
# The container is running with no config errors
pct status 200
pct config 200
# Networking inside the container came up
pct exec 200 -- ip a
pct exec 200 -- ping -c 3 8.8.8.8
# Real-time resource usage
pct exec 200 -- free -m
If pct status shows running, the container has an IP address and responds to ping, and free -m shows the expected amount of memory, the container is configured and working normally.
Frequently asked questions
Can a running LXC container later be converted into a full VM if the workload outgrows it?
Proxmox has no automatic conversion from a container to a virtual machine: LXC and VM rely on different storage and boot mechanisms. In practice people move the content instead — files, databases, application configuration — into a new VM by hand or through a backup, then stop and remove the original container once the move is done.
Is it safe to run a production database inside an LXC container?
Technically yes, with a caveat: an unprivileged container on a recent host kernel with properly configured storage, such as ZFS or thin LVM, handles a database about as well as a VM. The risk grows when guaranteed hard isolation from other containers on the same host matters — then a dedicated VM or even a physical server makes more sense.
Can you create several containers from the same template without conflicts between them?
Yes, several containers built from the same template coexist without conflict as long as each one gets its own numeric ID, hostname, and network address. A conflict only shows up when two containers end up with the same IP through DHCP or the same hostname on a shared network — an ordinary networking issue, not a limitation of LXC itself.
Can an unprivileged container see the processes and resources of other containers on the same host?
No, namespaces isolate each container's processes and resources from the others: from inside one container you cannot see the neighbor's processes, even when both run on the same host. For an unprivileged container that isolation is reinforced further by the UID/GID shift, which keeps the risk of affecting neighbors low even if something goes wrong inside the container itself.