Trying out LXD containers on VPS

BoostThis, Administrator

Containers are quite lightweight, so it’s interesting to see how many we can squeeze. We are going to use ZFS for the storage of the containers, stored on a file and not a block device. Here is what we are doing today,

Set up LXD on a 512MB RAM/20GB diskspace VPS

Create a container with a web server

Expose the container service to the Internet

Visit the webserver from our browser

We create the VPS, and we connect with

$ ssh root@ # change with the IP address you get

Welcome to Ubuntu 16.04 LTS (GNU/Linux 4.4.0-24-generic x86_64)

root@ubuntu-512mb-ams3-01:~# apt update && apt upgrade -y

root@ubuntu-512mb-ams3-01:~# apt policy lxdlxd:

root@ubuntu-512mb-ams3-01:~# apt install zfsutils-linux

We installed zfsutils-linux in order to be able to use ZFS as storage for our containers. In this tutorial we are going to use a file as storage (still, ZFS filesystem) instead of a block device.

root@ubuntu-512mb-ams3-01:~# df -h /

Filesystem Size Used Avail Use% Mounted on/dev/vda1 20G 1.1G 18G 6% /

We got 18GB free diskspace, so let’s allocate 15GB for LXD.

root@ubuntu-512mb-ams3-01:~# lxd init

Name of the storage backend to use (dir or zfs): zfs

Create a new ZFS pool (yes/no)? yes

Name of the new ZFS pool: lxd-pool

Would you like to use an existing block device (yes/no)? no

Size in GB of the new loop device (1GB minimum): 15

Would you like LXD to be available over the network (yes/no)? no

Do you want to configure the LXD bridge (yes/no)? yes

We accept the default settings for the bridge configuration

Warning: Stopping lxd.service, but it can still be activated by:  lxd.socketLXD has been successfully configured.

We initialized LXD with the ZFS storage backend, we created a new pool and gave a name (here, lxd-pool), we do not have a block device, so we get a (sparse) image file that contains the ZFS filesystem

We do not want now to make LXD available over the network and configure the LXD bridge for the inter-networking of the containters

Let’s create a new user and add them to the lxd group,

root@ubuntu-512mb-ams3-01:~# adduser ubuntu Adding user `ubuntu' ...

Make sure you add a good password, since we do not deal in this tutorial with best security practices. Many people use scripts on these VPSs that try common usernames and passwords.

We add the ubuntu user into the lxd group in order to be able to run commands as a non-root user.

root@ubuntu-512mb-ams3-01:~# adduser ubuntu lxd  (Adding user `ubuntu' to group `lxd' ...)

Adding user ubuntu to group lxd

We are now good to go. Log in as user ubuntu and run an LXD command to list images.


Create a Web server in a container

We launch (init and start) a container named c1.


The ubuntu:x in the screenshot is an alias for Ubuntu 16.04 (Xenial), that resides in the ubuntu: repository of images. You can find other distributions in the images repository.

As soon as the launch action was completed, I run the list action. Then, after a few seconds, I run it again. You can notice that it took a few seconds before the container actually booted and got an IP address.

Let’s enter into the container by executing a shell. We update and then upgrade the container.

ubuntu@ubuntu-512mb-ams3-01:~$ lxc exec c1 -- /bin/bash

root@c1:~# apt update && apt upgrade -y

Let’s install nginx, our Web server.

root@c1:~# apt install nginx

root@c1:~#Is the Web server running? Let’s check with the ss command (preinstalled, from package iproute2)

Let’s change a bit the default Web page,

root@c1:~# nano /var/www/html/index.nginx-debian.html


Expose the container service to the Internet

Now, if we try to visit the public IP of our VPS at we obviously notice that there is no Web server there. We need to expose the container to the world, since the container only has a private IP address.

The following iptables line exposes the container service at port 80.

Note that we run this as root on the VPS (root@ubuntu-512mb-ams3-01:~#), NOT inside the container (root@c1:~#).

iptables -t nat -I PREROUTING -i eth0 -p TCP -d --dport 80 -j DNAT --to-destination

Adapt accordingly the public IP of your VPS and the private IP of your container (10.x.x.x). Since we have a web server, this is port 80.

We have not made this firewall rule persistent as it is outside of our scope; see iptables-persistent on how to make it persistent.

Visit our Web server Here is the URL, so let’s visit it.


That’s it! We created an LXD container with the nginx Web server, then exposed it to the Internet.