Here are some notes on how I used LXD to run a container for WordPress. This is (a lot) more convenient than using Docker, which was my original approach to getting my WordPress site into a container. The main advantage for me is that a single container runs all the components together – no need for the ‘wiring’ between containers for each process.
There is a bash script that automates this at https://github.com/Kevin-Sangeelee/lxd-wordpress, and is a more complete description of the process since it automatically configures SSL/TLS and Exim.
Getting LXD onto Debian Stretch
LXD is installed on Debian via a Snap package, so
sudo apt-get install snapd if this is not already installed. See https://docs.snapcraft.io/installing-snap-on-debian. Then run
snap install lxd (see https://stgraber.org/2017/01/18/lxd-on-debian/) and log in again to get an updated command path to the new snap-installed binaries.
lxd init to configure the default environment, the storage type I chose was simply directories, since it’s the most convenient for moving files from the Docker setup that I’m migrating from.
Create and configure our container
When lxd is installed, create a new Debian container with
lxc launch 'images:debian/9' susanet-wp.
lxc exec susanet-wp passwd to set a root password, then
lxc console susanet-wp to log into the console. From here we can install the required packages.
apt-get install apache2 php-curl php-gd php-intl php-mbstring php-soap
php-xml php-xmlrpc php-zip libapache2-mod-php php-mysql
libphp-phpmailer mariadb-server mariadb-client iputils-ping
exim4-daemon-light curl wget netcat
From here it’s pretty much a normal WordPress installation. Since I was migrating from another database, the commands used to get MariaDB set up were as follows: –
create database wp_db
create user 'wp_user'@'localhost' identified by '<db password>'
grant all privileges on wp_db.* to 'wp_user'@'localhost'
I used this command to install my SQL dump file taken from the old Docker setup: –
zcat kakapo_wordpress_db.gz|lxc exec susanet-wp -- mysql wp_db
Some notes on LXD
LXD creates containers from locally stored images, though these images might themselves be fetched from a remote server.
There are a number of pre-configured public repositories, which can be viewed with
lxc remote list, and if you have another LXD installation elsewhere, then this can be used as a further remote server.
The command to register a new remote server is
lxc remote add myremote 10.81.1.4, where the IP address is that of another server running LXD, and ‘myremote’ is the alias by which I want to refer to the remote server.
Note that the remote server must be exposed on port 8443 (by default) of the specified IP. A password also has to be defined – clients will be prompted for this when adding this remote server. The following commands will configure the remote server.
lxc config unset core.https_address
lxc config set core.https_address [::]:8443
lxc config set core.trust_password <my_remote's password>
A snapshot in LXD refers to the state of a container as at a specific point in time, and can be used to easily restore the state of the container.
An image can be created from a stopped container, or from a snapshot of a running container. The following commands are listed as examples of usage: –
lxc snapshot susanet-wp my-snapshot
lxc publish susanet-wp/my-snapshot --alias my-new-image
lxc delete susanet-wp/my-snapshot
snapshot command takes a snapshot of the given container. The
publish command creates a local image from this snapshot, and the
delete command removes the snapshot (assuming you no longer want it).
Putting the above together, this can be used to copy a container to a backup server. The main local server would be configured to bind to an IP address/socket and given a password, and the backup server adds this as a remote. It can then ‘launch’ this image.
Alternatively, it’s even possible to simply push a local image to a backup server: –
lxc launch my-new-image myremote:susanet-backup
In this case, my local image ‘my-new-image’ is created on a remote server I aliased as ‘myremote’, and the new container on the remote server is called ‘susanet-backup’.
Networking to the outside world
A container can be given an interface on the bridge using something like the following: –
lxc config device add susanet-wp eth0 nic name=eth0 nictype=bridged parent=lxdbr0
We can use DNAT to forward host ports (e.g. on an external IP address) to the bridged interface using something like the following: –
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT \
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j DNAT \
A possibly simpler and more convenient way to connect the container to an external IP address is to use the ‘proxy’ device. This connects an ip:port address on the host to an ip:port in the container, so: –
kevin@vps1:~$ lxc config device add susanet-wp http proxy \
kevin@vps1:~$ lxc config device add susanet-wp https proxy \
would connect port 80 on all host interfaces to port 80 on the container’s localhost interface.
A note on UFW
ufw (The Uncomplicated Firewall), then the default ‘forward’ policy is to drop packets, meaning that no outgoing traffic will be routed from the guest to the Internet.
A rule will be required to forward packets between interfaces, otherwise just allow forwarding by default. Edit
/etc/default/ufw and set
ufw reload to enable the policy. Change it back to “DROP” if you want to revoke outgoing traffic.