Imagine a bacon-wrapped Ferrari. Still not better than our free technical reports.

Pragmatic DevOps: How to create your first environment with Chef and Vagrant

If you are following our blog regularly (thanks!), then you might have read our recent coverage on the benefits of having automated environment provisioning & virtualization. To put more meat on the bones, that post was followed up by bootstrapping guides for Vagrant and Chef. Today, we will fill in the final missing link – how to combine those two tools and create your first virtualized environment in a fully-automated fashion.


We assume you have successfully installed Vagrant — if that is not the case, please refer back to the previous blog post for instructions on installing Vagrant. What’s awesome is that you don’t need to worry about installing Chef, Vagrant will take care of that automatically for you.

Getting started – create a Vagrant project

First, we need to create a new Vagrant project – here we are basically going to quickly fast-forward you through the instructions in the original Vagrant post. If you still have that project around, feel free to jump straight to the next section.

So, let’s get it over with and just execute the following commands:

mkdir my-env
cd my-env
vagrant init precise32

As a result, you should have a Vagrantfile in the current directory with a bunch of comments and the following active lines:

Vagrant.configure("2") do |config| = "precise32"
    config.vm.box_url = ""

You can check the original Vagrant post for full explanation of the above steps and the meaning of the Vagrantfile contents.

Adding Chef to the virtual kitchen

Now that we have a basic Vagrant project set up, let’s add Chef provisioning to it, so we can install new software packages to the bare-bones box in an automatic fashion. As an example, let’s ask Chef to provision Apache httpd for us by adding 4 lines to Vagrantfile:

Vagrant.configure("2") do |config| = "precise32"
    config.vm.box_url = ""
    config.vm.provision :chef_solo do |chef|
        chef.add_recipe "apache2"
        chef.json = { :apache => { :default_site_enabled => true } }

The added lines instruct Vagrant to use Chef Solo for provisioning (solo-mode is the best for getting started) and to provision “apache2” on the box. How does it know how to provision “apache2”? It looks for a cookbook in the cookbooks subdirectory in the current directory.

But wait, I do not have any cookbooks! Where is the nearest library? Have no fear, we’ve got you covered – if you want to provision some commonly used package (e.g. Apache httpd), the chances are that someone has already written a cookbook for it and you can head over to OpsCode Community website and look for it there. Of course, you can always look for it on the interwebs or roll your own.

But let’s not get carried away, we are trying to provision “apache2” package and, as that is readily available on the OpsCode site, we can use knife (a command-line tool for Chef that we also mentioned in the last post) to download the cookbook to the current directory:

knife cookbook site download apache2

If you do not have knife installed, you can also manually download the apache2 cookbook and place it in the current directory (“my-env”).

Next, create the cookbooks directory and extract the downloaded package into it:

mkdir cookbooks
tar xvzf apache2-*.tar.gz -C cookbooks

That’s it – if you were to bring the virtual machine up now, “apache2” module would be nicely provisioned on it. But there is still one final piece missing from this puzzle.

Add port forwarding

Although we have added all the instructions for installing and running Apache httpd, we still need some way to verify that it actually works on that virtual machine. How could we do that? Usually, you would open the website with a browser — but this site is contained inside a virtual machine, so how can we get access to it?

Yep, you guessed it – this is a common problem, so Vagrant has a solution built-in. It allows you to map a port on the host machine to a port on the virtual machine, forwarding all the traffic. So, let’s map port 8888 on localhost to port 80 on the virtual machine – fortunately, this is as simple as adding just one line to the Vagrantfile:

Vagrant.configure("2") do |config| = "precise32"
    config.vm.box_url = "" :forwarded_port, guest: 80, host: 8888
    config.vm.provision :chef_solo do |chef|
        chef.add_recipe "apache2"
        chef.json = { :apache => { :default_site_enabled => true } }

Elegant, isn’t it? :-)

See it in action!

Now we should be all set for trying it out. Let’s bring the machine up (this will also automatically provision it):

vagrant up

Or, if it is already up, just re-provision it:

vagrant provision

Now you can open http://localhost:8888/ on your machine and you should see the good-old “It works!” page.

Still, why should you go through all this trouble with Chef?

You might be wondering: “Well, can’t I just ssh into the Vagrant virtual machine, type in ‘sudo apt-get install apache2’ and be on my way – why all this trouble with Chef and recipes?”

That is a valid question, and let’s answer that by throwing away the freshly built virtual machine and recreating it from scratch with just two commands.

Destroy the machine (this will delete the virtual machine image, freeing up your precious disk space):

vagrant destroy

And now, recreate the machine:

vagrant up

With a single command, you now have a freshly built virtual machine, all configured and ready to go. Finally, you can reuse that Vagrantfile also with other virtualization/cloud providers, such as VMware Fusion or AWS.

Summing up

As you can see, it is quite easy to use Chef and Vagrant together in order to automatically create virtual machines from scratch — this combination is very powerful and enables you to treat your infrastructure as code, which is a huge step in the DevOps direction.

If you’d like to see more of this, know that we only started to scratch the surface of this vast topic, and we will follow up with an in-depth Rebel Labs report soon. So, stay tuned, leave comments below, and follow us on @Rebel_Labs for more frequent updates.

  • airtonix

    This is great, perhaps you could do a post that details this process while also running through building a simple custom cookbook ?

  • Steve Stonebraker

    i’ve been messing around with chef all morning getting no where. THANK YOU for this awesome tutorial. I’ve successfully provisioned my first chef instance! You’re the man!

  • exfromtheleft

    this is a great tutorial, the only thing missing is the integration of bershelf into this workflow

  • I really appreciated this article. It made things a lot simpler to get started. Thanks!

  • Jeff Nyman

    I’m a little late to this article so I don’t know how much has changed but when I follow the exact steps provided here, when I run “vagrant up” I’m told: “ERROR: Cookbook iptables not found. If you’re loading iptables from another cookbook, make sure you configure the dependency in your metadata”. In searching around, it’s relatively difficult to find a concise answer of what to do about this.

  • Jan Szoja

    Thank you. That article finally let me start vagrant with apache and chef. The very first time it didn’t work after i followed all steps, but it appeared to be some incompatible chef version according to So I just logged through ssh to my new created vagrant machine and executed: curl -L | sudo bash. That probably downgraded the chef solo version?! Not sure, but now it works all well ;)