So, let’s say you want to isolate some applications. And you don’t have the resources for enough HVMs such as KVM, XEN, VirtualBox, Vmware, etc. But you also don’t want to use Docker due to security concerns or because you need a “full” container that behaves like a VM, which Docker does not provide.
So, LXC containers it is! They provide something akin to a VM, while sharing resources with the host.
Please note, this guide is specifically using old school LXC, and **NOT** Canonical’s LXD!
Installation
On Debian/Ubuntu you’d simply run:
apt install lxc-utils
Now if you run ip link
, you should notice a new bridge called lxcbr0!
Creating containers
The next step is to create a container, we’ll call it testing, and we’ll download an OS image (you can specify these as answers: ubuntu, jammy, amd64 or arm64):
lxc-create -n testing -t download
Now let’s check if the container is there!
lxc-ls --fancy
We can see “testing” and the state is “stopped”, so let’s start it!
lxc-start -n testing
Fixing networking (firewall)
If you run lxc-ls --fancy
again, you should see the state as “running”. If you see no IPv4 and no IPv6, and you’re using ufw, we need to set up some rules:
ufw allow in on lxcbr0 to any port 67 proto udp comment 'LXC DHCP Server'
ufw allow in on lxcbr0 to any port 53 proto udp comment 'LXC DNS Server'
ufw route allow in on lxcbr0
ufw route allow out on lxcbr0
This will basically allow containers to get an IP from LXC, and also use the DNS server provided by LXC. Further, this will allow traffic forwarding to/from LXC containers (such that they can actually reach the internet).
Working with the container
OK, again, lxc-ls --fancy
to see if you now have an IP, got it? Let’s continue!
lxc-attach -n testing
Now, with that command, you should be in a shell inside the “testing” container. Everything you do now, happens in the container!
Once you’re done doing your stuff, hit CTRL + D to exit the container, just as you normally would exit a shell (or enter exit).
So, let’s leave the container now, and type:
lxc-info -n testing
You should now get some basic information.
Cool, now let’s stop the container:
lxc-stop -n testing
Autostart
Now what if we want it to autostart with the system every time? Easy! First stop the container, verify it’s stopped, then run:
systemctl enable --now lxc@testing
Now verify it’s running, if it is, that means everything is working as expected!
Take good note: Now that systemd is managing our container, we no longer use lxc-start|stop -n testing
and instead systemctl start|restart|stop lxc@testing
!
Limit RAM
Now, let’s modify the config to limit the container memory to 2 GB of RAM:
nano /var/lib/lxc/testing/config
And add:
# Limits
lxc.cgroup.memory.limit_in_bytes = 2000000000
lxc.cgroup2.memory.max = 2000000000
Now run systemctl restart lxc@testing
and now if you run free -h
inside the container, you should see 2 GB of RAM!
Note: If you get any errors about one of these not existing, remove the one it claims not to exist. The difference is in whether you use cgroups v1 or v2. You might get useful information on your setup by running lxc-checkconfig
!
Destroying Containers
OK, we’re done testing and don’t want the container anymore, so we run (this cannot be undone):
systemctl disable --now lxc@testing
lxc-destroy -n testing
Always make sure you really want to delete a container and that you got the name right, there is no prompt and this action cannot be undone!
Conclusion
LXC may seem daunting at first, but once you get the hang of it, it’s actually a very quick and easy way to get multiple VMs (containers, actually) running.
Please note that I run this blog in my free time. Please consider donating a cup of coffee if I helped you out! (:
Note: Before creating your first production container, I recommend reading the following article to increase the security of your setup:
@sindastra Just a quick question that has always interested me: what can LXC do that docker can't do?
This is a test to see if I can respond directly from WordPress to fedi. 🧐