July 18, 2013

Crafting your own vagrant-lxc base box

As I said before, “next generation” vagrant-lxc boxes should simplify the process of “promoting” existing containers to base boxes. To back that up I’ve wrote a detailed step-by-step for creating an Ubuntu Precise and Debian Squeeze base boxes from an Ubuntu Host and I’m pretty sure it is possible to reuse the ideas from this post to build base boxes for / from other distros that suits everyone’s needs.

I’ve followed the steps on an Ubuntu 12.10 VirtualBox VM and if you want to follow along you might want to grab yourself a copy of the same Vagrantfile I used and fire it up with vagrant up --provider=virtualbox. I’m also assuming that you’ll follow each step within the same shell session as we’ll be reusing some variables between steps.

If you want a “copy & paste version” of this post check this gist out

Bootstrapping the guest container rootfs

The first thing we’ll do is create the base container with lxc-create with vanilla templates, for Ubuntu guests that means:

sudo lxc-create -n ${RELEASE}-base -t ubuntu -- --release ${RELEASE} --arch ${ARCH}

And for Debian guests:

sudo lxc-create -n ${RELEASE}-base -t debian

Before you ask, I decided to set both a SUITE and a RELEASE variables because the debian template expects a SUITE env var to specify the release version and we’ll be referencing it by just $RELEASE on the rest of the post to make things easier :)

If you want to know more about the parameters a template accepts, try running lxc-create -t <template-name> -- --help or look into the scripts at /usr/share/lxc/templates.

To finish up this initial setup, some aditional steps are required to make sure the Debian containers will get to play well with Vagrant / vagrant-lxc:


# This fixes some networking issues
# See https://github.com/fgrehm/vagrant-lxc/issues/91 for more info
sudo sed -i -e "s/\(\s\+localhost\)/\1\n127.0.1.1\t${RELEASE}-base\n/g" ${rootfs}/etc/hosts

# This ensures that `/tmp` does not get cleared on halt
# See https://github.com/fgrehm/vagrant-lxc/issues/68 for more info
sudo chroot $rootfs /usr/sbin/update-rc.d -f checkroot-bootclean.sh remove
sudo chroot $rootfs /usr/sbin/update-rc.d -f mountall-bootclean.sh remove
sudo chroot $rootfs /usr/sbin/update-rc.d -f mountnfs-bootclean.sh remove

Configure the vagrant user

The Ubuntu template creates an ubuntu user by default and we’ll just rename it to vagrant in order to avoid the need to specify Vagrant’s config.default.username on our Vagrantfiles:

sudo chroot ${ROOTFS} usermod -l vagrant -d /home/vagrant ubuntu

The Debian template does not create any user so we’ll just add it with adduser:

sudo chroot ${ROOTFS} useradd --create-home -s /bin/bash vagrant

At this point it is a good idea to set vagrant as the password too since most boxes I’ve used had this set up like that. The following applies to both Debian and Ubuntu guests:

echo -n 'vagrant:vagrant' | sudo chroot ${ROOTFS} chpasswd

Set up SSH access and passwordless sudo

The steps above ensures the vagrant user exists, but we still need to set up SSH access by allowing connections using the default Vagrant SSH key and also enable passwordless sudo for things to work properly.

Before we get to that, make sure that sudo is installed on Debian guests and that the vagrant user belongs to the sudo group by running:

sudo chroot ${ROOTFS} apt-get install sudo -y --force-yes
sudo chroot ${ROOTFS} adduser vagrant sudo

If everything went fine, you should now be able to run the commands below to set up SSH access and enable passwordless sudo:

# Configure SSH access
sudo mkdir -p ${ROOTFS}/home/vagrant/.ssh
sudo wget https://raw.github.com/mitchellh/vagrant/master/keys/vagrant.pub -O ${ROOTFS}/home/vagrant/.ssh/authorized_keys
sudo chroot ${ROOTFS} chown -R vagrant:vagrant /home/vagrant/.ssh

# Enable passwordless sudo for users under the "sudo" group
sudo cp ${ROOTFS}/etc/sudoers{,.orig}
sudo sed -i -e \
      's/%sudo\s\+ALL=(ALL\(:ALL\)\?)\s\+ALL/%sudo ALL=NOPASSWD:ALL/g' \

This is basically enough for us to export the container’s rootfs and prepare the vagrant-lxc box but the container is pretty dumb rough and we can do some work to make it a better place :)

Add YourStuff™ to the base container

After the guest container has been created, bring it up with sudo lxc-start -n ${RELEASE}-base and log in using vagrant as both the user and password, once you are logged into the container you can do whatever you want with it. Below you’ll find some things that I found useful when building the current vagrant-lxc boxes.

Please note that all of the code below is meant to be run from inside the container

Avoid certificate warnings when installing packages on Debian guests

If you try to install some package on the container you’ll notice that a warning shows up saying that the packages cannot be authenticated. In order to make it go away you’ll need to install the ca-certificates package and run a apt-get update:

sudo apt-get install -y --force-yes ca-certificates
sudo apt-get update

Add some basic packages

Make your container a better place and install some “must-have” packages:

PACKAGES=(vim curl wget manpages bash-completion)
sudo apt-get install ${PACKAGES[*]} -y --force-yes

Install Chef

If you want to have Chef pre installed on the base box, you can run:

curl -L https://www.opscode.com/chef/install.sh -k | sudo bash

Install Puppet

If you want to have Puppet pre installed on the base box, you can run:

wget http://apt.puppetlabs.com/puppetlabs-release-stable.deb -O "/tmp/puppetlabs-release-stable.deb"
dpkg -i "/tmp/puppetlabs-release-stable.deb"
sudo apt-get update
sudo apt-get install puppet -y --force-yes

Free up some disk space

When you are done configuring the container, make sure you run the code below from within the container to reduce the rootfs / box size:

sudo rm -rf /tmp/*
sudo apt-get clean

Build box package

After configuring the container, shut it down by running sudo halt and follow the steps below from the host to build the .box file:

# Set up a working dir
mkdir -p /tmp/vagrant-lxc-${RELEASE}

# Compress container's rootfs
cd /var/lib/lxc/${RELEASE}-base
sudo tar --numeric-owner -czf /tmp/vagrant-lxc-${RELEASE}/rootfs.tar.gz ./rootfs/*

# Prepare package contents
cd /tmp/vagrant-lxc-${RELEASE}
sudo chown $USER:`id -gn` rootfs.tar.gz
wget https://raw.github.com/fgrehm/vagrant-lxc/master/boxes/common/lxc-template
wget https://raw.github.com/fgrehm/vagrant-lxc/master/boxes/common/lxc.conf
wget https://raw.github.com/fgrehm/vagrant-lxc/master/boxes/common/metadata.json
chmod +x lxc-template

# Vagrant box!
tar -czf vagrant-lxc-${RELEASE}.box ./*

Try it out

To make sure you are able to use the container with vagrant-lxc, try importing the base box and bring it up with:

mkdir -p /tmp/test-import && cd /tmp/test-import
vagrant init my-box /tmp/vagrant-lxc-${RELEASE}/vagrant-lxc-${RELEASE}.box
vagrant up --provider=lxc

If everything went fine, you should be able to SSH into it with vagrant ssh without issues.

When you are done with the base container, make sure you destroy it using sudo lxc-destroy -n ${RELEASE}-base to free up some disc space.

Coming up

Over the following weeks I’ll be going through the process of using this approach to build the “official” Ubuntu base boxes instead of some hand made Rake task and bash scripts. One of the good things about following this approach is that because the built-in Ubuntu template keeps a rootfs cache around, which means that the base container creation time gets a lot lower and I’m able to iterate over new base boxes a lot faster. After this is proven to work, I’ll try to get my head around Go and will look into adding support for building lxc root file systems for Packer.

That’s it! I hope this opens up for others to fill in the Wiki with instructions for building other distros and http://www.vagrantbox.es/ with pre built boxes :)

© Fabio Rehm 2013-2022

Powered by Hugo & Kiss.