We all know the rule in some form or another: three times and you automate. And although I try to apply it, I find myself repeating some things with every new project, like creating a new CI server.

One solution would be to have a centralized server for all your projects, but customers are mostly not willing to depend on services outside of their network, which means you are often stuck with having to create a local solution for each project.

Having done this three times, I decided to start a repository to store scripts to automate development machine setup. One command to get you a fully functional system you only need to tweak to suit your needs. This blog will show you how to quickly set up a Jenkins server on a local virtual machine.

Getting started

To get everything started you need at least the following two tools

  1. VirtualBox for virtualization.
    VirtualBox is a full-virtualization system that allows you to easily manage virtual machines, however the virtual maclhines can also be run headless through a manager using a commandline interface. This is what the next tool, vagrant, will use to manage virtual machine configuration.
  2. Vagrant for automated machine creation and provisioning.
    Vagrant allows you to define a virutal machine in a Vagrantfile, sort of like a Makefile for VirtualBox based virtual machines. A Vagrantfile contains information on how to set up the virtual machine and contains infomration like: what file to use as a filesystem image, what network and memory configuration the virtual machine need and how much memory should be used. Even though vagrant will boot and run the machines headless, there is nothing keeping you from starting, exporting or cloning the machine using the VirtualBox gui anytime you might feel like it.
  3. Git, because we placed the configuration files on Github.

After installing the required tools, clone the vagrantboxes repository

git clone git://
cd vagrantboxes

Configuring the VM we want

In the repository you will find a Vagrantfile, which configures the VM properties: memory, base image to use, and how to do provisioning. Vagrant will import the base image, configure the VM, boot it up and then kick off Puppet to provision the machine. Puppet will be instructed to load the manifests/default.pp file to provision the system. We need to edit this file, to include the elements we want on the VM.

Open manifests/defaults.pp in a text editor. This file is a Puppet manifest where we configure which things to include in the VM. All the include statements mentioned refer to a directory within the modules directory of the repository. By default, everything but the "update the APT cache" module is commented out by a hash (#) sign.

We want to have Jenkins installed, so we remove the comment sign before the statement include jenkins. Save the file, and we are ready to start the VM.

Open a terminal and head over to the git root directory. You should be next to the Vagrantfile and then run vagrant up. This will import the VM and start to install Jenkins. Pay close attention to the notice lines, keeping in mind that the IP address mentioned will probably be different:

notice: Scope(Class[Jenkins]): Jenkins will be at after a reboot
notice: Scope(Class[main]): You can connect to your guest services via:
notice: Scope(Class[main]): You reboot by using 'sudo reboot' after logging in with: vagrant ssh

The virtual machine is started up with two network devices: one NAT, allowing the VM to contact the internet, the other host-only using DHCP. We do this to allow you to contact the VM from your local machine (the host-only network adapter) without having to define each of the ports you want to use in the Vagrantfile. If you missed these lines and want to find out where your machine is at, you can log in via the locally redirected SSH port using vagrant ssh. At the command prompt, issue the command ip a, like so:

vagrant@vagrant-debian-squeeze64:~$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet scope host lo
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 08:00:27:03:32:cb brd ff:ff:ff:ff:ff:ff
inet brd scope global eth0
inet6 fe80::a00:27ff:fe03:32cb/64 scope link
valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 08:00:27:56:7c:ba brd ff:ff:ff:ff:ff:ff
inet brd scope global eth1
inet6 fe80::a00:27ff:fe56:7cba/64 scope link
valid_lft forever preferred_lft forever

Spot the address of the virtual machine on the Host-only network. You can verify this by using SSH to connect to the address with user vagrant and password vagrant.

Visit your newly installed Jenkins

We now have a VM running, but we won't be able to see Jenkins just yet. By default, installed services are not started after the installation (this to ensure that each Puppet module will have enough memory to install when you combine an arbitrary set of them). Reboot the machine to finish the installation:

vagrant ssh
sudo reboot

After that, you should have a fully functional Jenkins install, visit it using the url mentioned earlier in the output, possibly:

Project specific settings

Now comes the part where you have to add project specific settings. All those things you never end up doing more then three times. Before you do that, open SSH to the virtual machine and shut it down:

vagrant ssh
sudo poweroff

then, open the Virtualbox management GUI and clone the machine. This will give you a normal Virtualbox machine where you can do all the things you probably want to do for each project specifically:

  • Add special users
  • Add a development deployment key for reading a git repo
  • Install your own special kind of Java and maven
  • Configure host names you want to use
  • etc.

You can always log in with username vagrant and password vagrant. Any root activity should be prefixed with sudo without needing a password (or use sudo su - to start a root console).

Once your done, you can export the VM as an OVA image so you can move it to another host or import it into your local development cloud. Happy hacking and don't forget to look at the available modules and add your own!