With this series I would like to provide a comprehensive hands-on tutorial that explains step-by-step how to build a centrally managed Linux server infrastructure using Puppet.

Target audience

I believe that the readers who will get most out of this series are Linux systems administrators who already manage a small-sized network of server systems and wonder how they can grow the existing infrastructure – without having to grow the amount of time and effort put into managing these systems linearly with the number of new systems. Maybe you currently manage 5 server systems, and you ask yourself if there is a clever approach to manage 50 systems without growing your staff. You have heard about centralized configuration management and Puppet, and are eager to give it a try.


If you are sitting at a Linux, Windows or Mac OS X desktop system with at least 4 GB of RAM, 16 GB free disk space, and a CPU that is capable of running virtual machine guests, then you are already good to go. Everything that needs to be done and anything that you need will be provided through in the course of this series.

Getting started

I will simply skip the part where I try to sell you the idea of tool-based configuration management with Puppet. When you arrive at this text, you probably already know you want to use it. Let’s get our feet wet right away.

I promised a hands-on and comprehensive tutorial, and for this we need to prepare an environment that allows us to not only talk about using Puppet, but instead enables us to set up a real – albeit small – server infrastructure that runs an actual Puppet server that serves an actual Puppet client. In other words, we need two Linux virtual machines.

For this tutorial, I would like use two Ubuntu 12.04 LTS Precise Pangolin machines, simply because that’s the Linux distribution I have the most experience with personally. The steps described herein shouldn’t be much different if you use a more recent version of Ubuntu or, say, Debian 7.0 Wheezy.

The first thing to do is to download and install VirtualBox from Oracle. It’s a free virtual machine manager which we will use to run our two virtual Ubuntu systems. As of this writing, version 4.3.8-92456 is the current release of VirtualBox, so head over to http://download.virtualbox.org/virtualbox/4.3.8/ and download either VirtualBox-4.3.8-92456-Win.exe, VirtualBox-4.3.8-92456-OSX.dmg, or VirtualBox-4.3.8-92456-Linux_amd64.run, depending on the operating system you use. Just install it with default options once you’ve downloaded it.

Once VirtualBox is installed on your computer, it’s time to download the installation medium for Ubuntu 12.04 LTS Server 64bit. Go to http://releases.ubuntu.com/12.04/ and choose the link 64-bit PC (AMD64) server install CD to download it. We will use the same ISO to install both virtual machines.

Once the download has finished and the ISO file is available on your computer, start VirtualBox and select New… from the Machine menu.

We will start with installation of the virtual machine that is going to be the Puppet server, therefore, name the virtual machine puppetserver in the dialogue that pops up. As Type, choose Linux, and set Version to Ubuntu (64 bit). A memory size of 512 MB does the job. Let VirtualBox create a hard drive file for the VM – a size of 8 GB is more than enough.

You can now Start your newly created virtual machine. VirtualBox will ask for an installation medium – point it at the Ubuntu ISO you have downloaded.

VirtualBox will start the VM and boot into the Ubuntu installer CD. Choose English as the language used during the installation, and continue by choosing Install Ubuntu Server.

During the installation process, these are the options you should choose:

  • Language: English
  • Country: Simply choose your country
  • Locale: en_US.UTF-8
  • Keyboard: Choose the keyboard layout that best fits your needs
  • Default User: ubuntu, with password ubuntu
  • Encrypt home directory: No
  • Timezone: Choose the time zone you’re in
  • Partitioning: Guided – use entire disk
  • When asked what packages to install, only select OpenSSH server
  • Install grub into the MBR
  • Set the system clock to UTC
  • Do not install security updates automatically

Once the first virtual machine is completely installed, boot into it. Then, set up the second virtual machine, the one that will act as our puppet client. Proceed just as you did with the first machine, with one exception: In VirtualBox and within the Ubuntu installation process, name the second machine puppetclient instead of puppetserver.

Upon finishing the installation, boot into the second VM just as you did with the first one. This should result in two VirtualBox windows, both running Ubuntu 12.04, both offering you a login prompt. Next, we need to configure some VirtualBox settings, so please log into both machines (user: ubuntu, password ubuntu), and shut them down with sudo poweroff.


Because we are setting up a Puppet server and client, both machines need to be able to talk with each other over the network. Also, it would be nice to be able to reach both machines from our host computer – this way, we can SSH into the machines and are not forced to work on the rather small and uncomfortable VirtualBox console. Last but not least, both machines need to be able to reach the Internet in order to install packages.

To achieve all this, we need two virtual networks, a Host-only Network and a NAT Network. Both machines then need to have two ethernet adapters, eth0 for connecting to the Host-only Network, and eth1 to connect to the NAT Network.

The Host-only Network allows both machines to connect to each other, and allows us to connect to both machines from the host system. The NAT Network allows both machines to reach the Internet.

Within VirtualBox itself, both networks are set up. Choose File -> Preferences and go to Network. The tab NAT Networks does not list a network, but one is available anyways; no need to configure one. Change to tab Host-only Networks. Make sure that a network named vboxnet0 is availble. Click on the small screwdriver icon next to the list, and make sure that in the following popup, the Adapter and DHCP Server settings look as follows:

Our host computer automatically has the address on the Host-only Network. Our puppetserver machine should bind to, and the puppetclient to

In order to configure this, change into the Settings dialogue for both virtual machines. Goto Network, and set Adapter 1 to be attached to Host-only Adapter with Name set to vboxnet0. Then enable Adapter 2 and attach it to NAT (not NAT Network!). These settings must be identical for both machines.

Now boot up both machines and log in with the ubuntu user. We need to configure two network interfaces on both machines in /etc/network/interfaces. For the puppetserver, this looks like this:

/etc/network/interfaces on puppetserver

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static

auto eth1
iface eth1 inet dhcp

And this is how it must look on the puppetclient:

/etc/network/interfaces on puppetclient

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static

auto eth1
iface eth1 inet dhcp

As you can see, the only difference is the IP address. These settings will allow Internet access via eth1, and VM-to-VM connectivity with static IP addresses through eth0.

The last step is to add both machine’s names to the hosts file on both systems, which allows us to address them by name instead of IP addresses. This is how /etc/hosts should look on the puppetserver:

/etc/hosts on puppetserver       localhost       puppetserver    puppetclient

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

And on the puppetclient:

/etc/hosts on puppetclient       localhost       puppetclient    puppetserver

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

After rebooting both virtual machines, you should now be able to ping puppetclient from the puppetserver and vice versa, and from your host computer, you should be able to ping and, respectively.

Setting up the Puppet server

Now that the virtual machines for the Puppet server and the Puppet client are set up with a basic Ubuntu installation and networking is up and running, we can start to set up the Puppet server system.

Everything that is needed for our server system to actually act as a Puppet server is provided through the Ubuntu software repositories – no manual software installation or fiddling with external packages is required.

To install the neccessary software packages, log in to the puppetserver VM as user ubuntu and run the following commands:

On the puppetserver VM

~# sudo apt-get update
~# sudo apt-get install puppetmaster

No further steps are neccessary – the Puppet server runs with a reasonable default config that will do for now.

Setting up the Puppet client

Getting the Puppet client (or, in Puppet lingo, agent) up and running on our puppetclient machine isn’t that much different. First, we need to install the Puppet client package:

On the puppetclient VM

~# sudo apt-get update
~# sudo apt-get install puppet

Then, we need to tell the agent what the domain name of the Puppet server is, by editing /etc/puppet/puppet.conf. All we need to do is to add the line server=puppetserver:

/etc/puppet/puppet.conf on puppetclient


# These are needed when the puppetmaster is run by passenger
# and can safely be removed if webrick is used.
ssl_client_header = SSL_CLIENT_S_DN 
ssl_client_verify_header = SSL_CLIENT_VERIFY

With this, the client is capable of connecting to the Puppet master process on puppetserver, but it can’t really talk to the server yet, because it’s not in the list of verified and allowed clients. To achieve this, we need to make a very first connection from the client to the server:

On the puppetclient VM ~# sudo puppet agent --verbose --no-daemonize --onetime

This starts a connection to the Puppet master process that is listening on port 8140 on puppetserver. The output will be verbose, and the agent will not continue running in the background as a daemon. Also, it will run only onetime, that is, after the connection is closed, the agent process will exit.

If everything has been correctly set up, then the output of this first run will look like this:

On the puppetclient VM, after running the Puppet agent

info: Creating a new SSL key for puppetclient
info: Creating a new SSL certificate request for puppetclient
info: Certificate Request fingerprint (md5): 20:74:A7:BD:69:5D:50:8D:6A:79:67:6E:DC:5E:41:E0
Exiting; no certificate found and waitforcert is disabled

This sounds a little bit like an error, but it isn’t – the client has made itself known to the server, but the server has not yet accepted the client. This is the next step; we must sign the SSL certificate request that the puppetclient has created and sent to the server. We can see a list of yet-to-be-signed certificate requests on the server:

On the puppetserver VM ~# sudo puppet cert --list

This will print the the following list:

On the puppetserver VM "puppetclient" (20:74:A7:BD:69:5D:50:8D:6A:79:67:6E:DC:5E:41:E0)

We can now sign the request, which will allow the client to actually retrieve information from the server upon subsequent connections:

On the puppetserver VM

~# sudo puppet cert --sign puppetclient

notice: Signed certificate request for puppetclient
notice: Removing file Puppet::SSL::CertificateRequest puppetclient at '/var/lib/puppet/ssl/ca/requests/puppetclient.pem'

Switching back to the puppetclient, we are now able to initiate a full connection to the server:

On the puppetserver VM

~# sudo puppet agent --verbose --no-daemonize --onetime

info: Caching certificate for puppetclient
info: Caching certificate_revocation_list for ca
info: Caching catalog for puppetclient
info: Applying configuration version '1395687915'
info: Creating state file /var/lib/puppet/state/state.yaml
notice: Finished catalog run in 0.02 seconds

And that’s it – our Puppet infrastructure is fully up and running. However, there is nothing yet within that infrastructure that actually does anything. Our goal is to manage the configuration of our puppetclient through the Puppet master running on the puppetserver. But we have not yet defined any content that could trigger a configuration change on the client. This is what we will do next, in Part 2 of Building manageable server infrastructures with Puppet.

If you would like to be informed on updates to this post, just follow @manuelkiessling