How to create unprivileged LXC container on Ubuntu Linux 14.04 LTS

Posted on in Categories , last updated February 3, 2016

How do I install, create and manage unprivileged LXC containers on Ubuntu Linux version 14.04 LTS server?

LXC is an acronym for Linux Containers. It is nothing but an operating system-level virtualization technology for running multiple isolated Linux distros (systems containers) on a single Linux host. In this tutorial you will learn how to install and manage LXC containers on Ubuntu Linux server.

Say hello to LXC

Fig.01: Linux containers
Fig.01: Linux containers

The LXC often described as a lightweight virtualization technology. You can think LXC as chrooted jail on steroids. There is no guest operating system involved. You can only run Linux distros with LXC. You can not run Windows or BSD or any other operating system with LXC. You can run CentOS or Gentoo or any other Linux distro using LXC.

Traditional virtualization such as KVM/XEN/VMWARE and paravirtualization need a full operating system image for each instance. You can run any operating system using traditional virtualization.

Install the lxc on Ubuntu

Type the following apt-get command to install :
$ sudo apt-get install lxc
Sample outputs:

Fig.02: Installing LXC on Ubuntu
Fig.02: Installing LXC on Ubuntu

LXC and networking

LXC creates a NATed bridge called lxcbr0 for you. Each container will have one veth NIC and all traffic routed using the lxcbr0 bridge. To view current settings, enter:
$ sudo brctl show
Sample outputs:

bridge name	bridge id		STP enabled	interfaces
lxcbr0		8000.fe09977d9e4f	no		vethH1OXMH

To see an IP address assigned to the lxcbr0, enter:
$ sudo ifconfig lxcbr0
Sample outputs:

lxcbr0    Link encap:Ethernet  HWaddr fe:09:97:7d:9e:4f  
          inet addr:  Bcast:  Mask:
          inet6 addr: fe80::4820:9fff:fe01:4d52/64 Scope:Link
          RX packets:40 errors:0 dropped:0 overruns:0 frame:0
          TX packets:42 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:3553 (3.5 KB)  TX bytes:4383 (4.3 KB)

To see DHCP range used by containers, enter:
$ ps aux | grep lxc-dns | grep -o 'dhcp-range.[0-9].* '
Sample outputs:

dhcp-range, --dhcp-lease-max=253 --dhcp-no-override --except-interface=lo --interface=lxcbr0 --dhcp-leasefile=/var/lib/misc/dnsmasq.lxcbr0.leases

To check the current kernel for lxc support, enter:
$ lxc-checkconfig
Sample outputs:

Kernel configuration not found at /proc/config.gz; searching...
Kernel configuration found at /boot/config-3.13.0-76-generic
--- Namespaces ---
Namespaces: enabled
Utsname namespace: enabled
Ipc namespace: enabled
Pid namespace: enabled
User namespace: enabled
Network namespace: enabled
Multiple /dev/pts instances: enabled
--- Control groups ---
Cgroup: enabled
Cgroup clone_children flag: enabled
Cgroup device: enabled
Cgroup sched: enabled
Cgroup cpu account: enabled
Cgroup memory controller: enabled
Cgroup cpuset: enabled
--- Misc ---
Veth pair device: enabled
Macvlan: enabled
Vlan: enabled
Bridges: enabled
Advanced netfilter: enabled
--- Checkpoint/Restore ---
checkpoint restore: enabled
File capabilities: enabled
Note : Before booting a new kernel, you can check its configuration
usage : CONFIG=/path/to/config /usr/bin/lxc-checkconfig

Creating unprivileged container

Unprivileged containers run the same way as privileged ones, simply without using sudo or root access. This is more secure as you can’t be root on the host even if you managed to escape container. The steps are as follows:

  1. Create a new user for lxc.
  2. Set password for for lxc.
  3. Find out allocated subuids and subgids for the lxc user.
  4. Create a default container configuration file for lxc user
  5. Create a new container.
  6. Start a new container.
  7. Deploy apps in newly created unprivileged container.

Step – 1: Create a new user for lxc

Type the following command to add a user called mylxcusr:
$ sudo useradd -s /sbin/bash -c 'unprivileged lxc user' -m mylxcusr

Step – 2: Set password for for lxc

Set the password for mylxcusr
$ sudo passwd mylxcusr

Step -3: Find out allocated subuids and subgids for the lxc user

Type the following command
$ sudo grep mylxcusr /etc/sub{gid,uid}
Sample outputs:


Note down the values.

Step – 4:Create a default container configuration file for lxc user

Make sure the user “mylxcusr” is allowed up to 10 veth type devices to be created and added to the bridge called lxcbr0. In other words networking will only work if you add the following lines:
$ sudo vi /etc/lxc/lxc-usernet
Append the following line:

mylxcusr veth lxcbr0 10

Save and close the file. Now switch to new user using su or just login using the ssh client:
$ su - mylxcusr
$ ssh [email protected]
$ ssh [email protected]
$ id

Sample outputs:

uid=1002(mylxcusr) gid=1002(mylxcusr) groups=1002(mylxcusr)

Once logged into a remote machine, type the following command to create ~/.config/lxc/ director as follows:
$ mkdir -p ~/.config/lxc
Finally, create ~/.config/lxc/default.conf file as follows:
$ cp /etc/lxc/default.conf ~/.config/lxc/default.conf
Edit the file, enter:
$ vi ~/.config/lxc/default.conf
Append the configuration as follows (use mapped user and group id ranges 100000:65536 from step #3):

lxc.id_map = u 0 100000 65536
lxc.id_map = g 0 100000 65536

Step – 5:Create a new container

Let us create a new Ubuntu container called httpd, enter:
$ lxc-create -t download -n httpd -- -d ubuntu -r trusty -a amd64
Sample outputs:

Fig.03: Create an Ubuntu container
Fig.03: Create an Ubuntu container

That’s all it takes to create an Ubuntu container called httpd.

Step – 6:Start a new container

To start httpd container type:
$ lxc -n httpd -d
$ echo $?
$ lxc-ls --fancy

httpd     RUNNING  -     NO

To start a process inside a running container or just to login, enter:
$ lxc-attach -n httpd
Sample session inside a httpd container:

[email protected]:/# id
uid=0(root) gid=0(root) groups=0(root)
[email protected]:/# ifconfig 
eth0      Link encap:Ethernet  HWaddr 00:16:3e:ea:ce:fa  
          inet addr:  Bcast:  Mask:
          inet6 addr: fe80::216:3eff:feea:cefa/64 Scope:Link
          RX packets:37 errors:0 dropped:0 overruns:0 frame:0
          TX packets:35 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:3502 (3.5 KB)  TX bytes:3362 (3.3 KB)
lo Link encap:Local Loopback inet addr: Mask: inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
[email protected]:/# lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 14.04.3 LTS Release: 14.04 Codename: trusty

For security reason, container images ship without user accounts and without a root password. Setup a root password for httpd container:
# passwd root
To enable sshd for httpd container, run:
# apt-get install openssh-server
You can login from host to container using ssh:
$ ssh [email protected]

Step – 7:Deploy apps in newly created unprivileged container

You can now install nginx, php or any other application. Just attach to container and run commands or apps as per your need.

Important management commands

Here is a quick overview of useful commands:

How do I start a container?

$ lxc-start -n {container-name-here} -d
$ lxc-start -n mysql -d

How do I stop a container?

$ lxc-stop -n {container-name-here}
$ lxc-stop -n mysql

How do I destroy (delete) a container?

$ lxc-destroy -n {container-name-here}
$ lxc-destroy -n mysql

How do I list all containers?

$ lxc-ls
$ lxc-ls --fancy

How do I update or patch my container?

Use the lxc-attach command to update your container that is powered by Ubuntu or Debian:
$ lxc-attach -n mysql apt-get -- -qq update
$ lxc-attach -n mysql apt-get -- -qq upgrade
$ lxc-attach -n nginx yum -- -y update

How do I force DHCP to provide a persistent IP address to my container?

As a root on host run the following command:
$ sudo vi /etc/lxc/dnsmasq.conf
To give containers on lxcbr0 a persistent ip address based on domain name, you can add entries as follows:


Save and close the file.

How do I create CentOS/Gentoo/Fedora or any other flavour of LXC?

$ lxc-create -t download -n {container-name-here} -- -d {DISTRONAME} -r {RELEASE} -a {ARCH}
$ lxc-create -t download -n bar -- -d centos -r 6 -a amd64
$ lxc-create -t download -n foo -- -d gentoo -r current -a amd64
$ lxc-create -t download -n db -- -d ubuntu -r precise -a i386
$ lxc-create -t download -n nginx -- -d debian -r jessie -a amd64
$ lxc-create -t download -n mysql -- -d fedora -r 22 -a amd64
$ lxc-create -t download -n maridb -- -d oracle -r 6.5 -a amd64
$ lxc-create -t download -n cahcing -- -d plamo -r 5.x -a amd64

The list of lxc flavour:

centos	6	amd64	default	20160203_02:16
centos	6	i386	default	20160203_02:16
centos	7	amd64	default	20160203_02:16
debian	jessie	amd64	default	20160202_22:42
debian	jessie	armel	default	20160111_22:42
debian	jessie	armhf	default	20160111_22:42
debian	jessie	i386	default	20160202_22:42
debian	sid	amd64	default	20160202_22:42
debian	sid	armel	default	20160111_22:42
debian	sid	armhf	default	20160111_22:42
debian	sid	i386	default	20160202_22:42
debian	squeeze	amd64	default	20160202_22:42
debian	squeeze	armel	default	20150826_22:42
debian	squeeze	i386	default	20160202_22:42
debian	wheezy	amd64	default	20160202_22:42
debian	wheezy	armel	default	20160111_22:42
debian	wheezy	armhf	default	20160111_22:42
debian	wheezy	i386	default	20160202_22:42
fedora	21	amd64	default	20160203_01:27
fedora	21	armhf	default	20160112_01:27
fedora	21	i386	default	20160203_01:27
fedora	22	amd64	default	20160203_01:27
fedora	22	armhf	default	20160112_01:27
fedora	22	i386	default	20160203_01:27
gentoo	current	amd64	default	20160203_14:12
gentoo	current	armhf	default	20160111_14:12
gentoo	current	i386	default	20160203_14:12
oracle	6.5	amd64	default	20160203_11:40
oracle	6.5	i386	default	20160203_11:40
plamo	5.x	amd64	default	20160202_21:36
plamo	5.x	i386	default	20160202_21:36
ubuntu	precise	amd64	default	20160203_03:49
ubuntu	precise	armel	default	20160112_03:49
ubuntu	precise	armhf	default	20160203_03:49
ubuntu	precise	i386	default	20160203_03:49
ubuntu	trusty	amd64	default	20160203_03:49
ubuntu	trusty	arm64	default	20150604_03:49
ubuntu	trusty	armhf	default	20160203_03:49
ubuntu	trusty	i386	default	20160203_03:49
ubuntu	trusty	ppc64el	default	20160201_03:49
ubuntu	vivid	amd64	default	20160203_03:49
ubuntu	vivid	arm64	default	20150604_03:49
ubuntu	vivid	armhf	default	20160203_03:49
ubuntu	vivid	i386	default	20160203_03:49
ubuntu	vivid	ppc64el	default	20160201_03:49
ubuntu	wily	amd64	default	20160203_03:49
ubuntu	wily	arm64	default	20150604_03:49
ubuntu	wily	armhf	default	20160203_03:49
ubuntu	wily	i386	default	20160203_03:49
ubuntu	wily	ppc64el	default	20160201_03:49
ubuntu	xenial	amd64	default	20160203_03:49
ubuntu	xenial	armhf	default	20160203_03:49
ubuntu	xenial	i386	default	20160203_03:49


And, there you have it, a container running on Ubuntu Linux 14.04 LTS in unprivileged mode. I suggest you visit the official project home page and Ubuntu lxc wiki page for more information.

7 comment

  1. Hi, very nice detailed article. As it is FAQ directory I think it should have one of top questions: “how is it different to docker?”. Docker went mainstream and it is much easier for readers of your article to have a picture of `lxc` from the known docker point of view. Cheers

  2. > Step – 2: Set password for for lxc
    > Set the password for mylxcusr
    > $ sudo mylxcusr

    $ sudo passwd mylxcusr

  3. when I start: lxc -n httpd -d, it tells me: lxc-start: lxc_start.c: main: 344 The container failed to start.
    If I sttart the command with the debugging enabled, it tells me: lxc-start 1458080603.878 ERROR lxc_start – start.c:__lxc_start:1213 – failed to spawn ‘httpd’

  4. Host Debian Stretch 64b
    Unprivileged Container – Debian Jessie 64
    Unprivileged containers start, stop, attach bla bla bla
    However from within container apt-get install fails with

    debconf: delaying package configuration, since apt-utils is not installed
    dpkg: warning: ‘ldconfig’ not found in PATH or not executable
    dpkg: warning: ‘start-stop-daemon’ not found in PATH or not executable
    dpkg: error: 2 expected programs not found in PATH or not executable
    Note: root’s PATH should usually contain /usr/local/sbin, /usr/sbin and /sbin
    E: Sub-process /usr/bin/dpkg returned an error code (2)

Leave a Comment