1. Debian Host Setup
Install LXC
apt update && apt install -y lxc debootstrap bridge-utils iptables-persistent
This pulls in:
- lxc — the container runtime
- debootstrap — builds base system from apt repositories
- bridge-utils — for brctl
- iptables-persistent — makes iptables rules survive reboots
Enable IP forwarding
echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.d/99-lxc.conf
sysctl -p /etc/sysctl.d/99-lxc.conf
Check cgroup version
Debian 12 (Bookworm) uses cgroup2 by default. Verify:
cat /sys/fs/cgroup/cpu.max
# Expected: "100000 100000" or similar (quota 100ms per 100ms period = 1 CPU)
If this returns content, cgroup2 cpu controller is active — the lxc.cgroup2.cpu.max setting will work.
Create bridge (br0 = LAN)
Edit /etc/network/interfaces:
# /etc/network/interfaces — eth0 is the host's physical WAN/NIC
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet manual
auto br0
iface br0 inet static
address 192.168.2.1/24
gateway 192.168.2.254
bridge-ports eth0
bridge-stp off
bridge-fd 0
Warning: If you're SSH'd into this machine and change the interface that carries your SSH session, you may lose connectivity. Do this from console or have fallback access.
Restart networking:
systemctl restart networking
Verify:
ip addr show br0
Set FORWARD policy to ACCEPT
iptables -P FORWARD ACCEPT
# This survives reboots because of iptables-persistent installed above
2. Create Container
Option A — download template (recommended)
apt install -y lxc-templates
lxc-create -n debian-ct -t download -- -d debian -r bookworm -a amd64
This downloads a pre-built Debian rootfs. After creation, configure the network (section 3) and start.
Option B — debootstrap (manual rootfs)
apt install -y debootstrap
mkdir -p /var/lib/lxc/debian-ct/rootfs
debootstrap bookworm /var/lib/lxc/debian-ct/rootfs http://deb.debian.org/debian
Then configure the LXC config manually (section 3).
3. Configure Networking Inside Container
With lxc-create -t download, the rootfs is at /var/lib/lxc/debian-ct/rootfs. Edit the network config:
nano /var/lib/lxc/debian-ct/rootfs/etc/network/interfaces
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 192.168.2.10/24
gateway 192.168.2.254
Or DHCP:
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet dhcp
Set hostname:
echo 'debian-ct' > /var/lib/lxc/debian-ct/rootfs/etc/hostname
Add hosts entry:
cat > /var/lib/lxc/debian-ct/rootfs/etc/hosts << 'EOF'
127.0.0.1 localhost
127.0.1.1 debian-ct
EOF
4. LXC Config
nano /var/lib/lxc/debian-ct/config
# ── UPDATE THESE 3 LINES before starting ──
lxc.uts.name = debian-ct # ← container name (change this)
lxc.rootfs.path = dir:/var/lib/lxc/debian-ct/rootfs # ← must match
lxc.net.0.hwaddr = 00:11:22:33:44:55 # ← generate: openssl rand -hex 6 | sed 's/\(..\)/\1:/g'
lxc.include = /usr/share/lxc/config/debian.conf
lxc.arch = linux64
# Memory cap
lxc.cgroup2.memory.max = 512M
lxc.cgroup2.memory.high = 384M
# CPU & process limits
lxc.cgroup2.cpu.weight = 102
lxc.cgroup2.cpu.max = "1 4"
lxc.cgroup2.memory.swap.max = 256M
lxc.cgroup2.pids.max = 1024
# Autostart on boot
lxc.start.auto = 1
lxc.start.delay = 0
# Network: veth → br0
lxc.net.0.type = veth
lxc.net.0.link = br0
lxc.net.0.flags = up
# Mount auto — let lxcfs handle cgroup/proc/sys automatically
lxc.mount.auto = proc:mixed sys:mixed cgroup:rw:force
Generate a unique MAC address:
bash openssl rand -hex 6 | sed 's/\(..\)/\1:/g'
5. Start / Stop the Container
# Start (daemonized — runs in background)
lxc-start -n debian-ct -d
# Check status
lxc-info -n debian-ct
# Stop (10s timeout before SIGKILL)
lxc-stop -n debian-ct -t 10
# Start in foreground (see all logs live — useful for debugging)
lxc-start -n debian-ct -F
# Enter the container (run commands inside)
lxc-attach -n debian-ct
-d= daemonize (background).-F= foreground (debugging).-t N= timeout seconds on stop.
6. Known Issues & Fixes
Issue: "Failed to set cpu.max"
On some Debian setups the cpu controller is in cgroup1 (no cpu.max file). Comment out the line:
# In /var/lib/lxc/debian-ct/config, comment out:
# lxc.cgroup2.cpu.max = "1 4"
Verify on your host:
cat /sys/fs/cgroup/cpu.max
# If empty or "max", cgroup2 cpu controller is not active
Issue: "Address not available" on MAC assignment
Remove the lxc.net.0.hwaddr line or use a different MAC. Let LXC auto-generate one.
Issue: Container gets no IP from DHCP
Make sure isc-dhcp-client is installed inside the container:
lxc-attach -n debian-ct -- apt install -y isc-dhcp-client
Issue: SSH keys conflict on rebuild
When rebuilding a container, SSH host keys are regenerated on first boot. Remove old keys inside the container:
rm -f /var/lib/lxc/debian-ct/rootfs/etc/ssh/ssh_host_*
7. Alpine vs Debian Differences
| Aspect | Alpine | Debian |
|---|---|---|
| Package manager | apk |
apt |
| Init system | OpenRC | systemd |
| Cgroup setup | May need manual cgroup2 mount | cgroup2 by default (Bookworm+) |
| CPU cgroup | cpu.max may not exist |
Usually works out of box |
| Network config | /etc/network/interfaces |
/etc/network/interfaces |
| LXC templates | lxc-create -t download |
lxc-create -t download |
| Firewall | iptables / nftables |
iptables / nftables |
Quick Reference — Full Config
Host /etc/network/interfaces:
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet manual
auto br0
iface br0 inet static
address 192.168.2.1/24
gateway 192.168.2.254
bridge-ports eth0
bridge-stp off
bridge-fd 0
LXC /var/lib/lxc/debian-ct/config:
# ── UPDATE THESE 3 LINES before starting ──
lxc.uts.name = debian-ct # ← container name (change this)
lxc.rootfs.path = dir:/var/lib/lxc/debian-ct/rootfs # ← must match
lxc.net.0.hwaddr = 00:11:22:33:44:55 # ← generate: openssl rand -hex 6 | sed 's/\(..\)/\1:/g'
lxc.include = /usr/share/lxc/config/debian.conf
lxc.arch = linux64
# Memory cap
lxc.cgroup2.memory.max = 512M
lxc.cgroup2.memory.high = 384M
# CPU & process limits
lxc.cgroup2.cpu.weight = 102
lxc.cgroup2.cpu.max = "1 4"
lxc.cgroup2.memory.swap.max = 256M
lxc.cgroup2.pids.max = 1024
# Autostart on boot
lxc.start.auto = 1
lxc.start.delay = 0
# Network: veth → br0
lxc.net.0.type = veth
lxc.net.0.link = br0
lxc.net.0.flags = up
# Mount auto — let lxcfs handle cgroup/proc/sys automatically
lxc.mount.auto = proc:mixed sys:mixed cgroup:rw:force
Container /etc/network/interfaces:
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 192.168.2.10/24
gateway 192.168.2.254