Chef Solo allows you to provision your virtual machine with Chef Cookbooks without requiring a Chef Server. At a very basic level, chef is an open source systems integration framework which automates tasks through programmable “cookbooks.” This page will not go into the details of creating custom chef cookbooks, since that is covered in detail around the web, but a good place to start is the opscode cookbooks repository which contains cookbooks for most of the popular server software already made.
First, Vagrant needs to know where the cookbooks are located. By default, Vagrant will look in the “cookbooks” directory relative to the root of the project directory (where a project’s Vagrantfile is). The cookbooks directory should have a structure similar to the following:
$ ls cookbooks/
apache2/
passenger_apache2/
rails/
sqlite/
vagrant_main/
Basically, the cookbooks directory should immediately contain all the folders of the various cookbooks.
To tell Vagrant what the cookbook path is, set it up in your Vagrantfile, like so:
Vagrant::Config.run do |config|
config.vm.provision :chef_solo do |chef|
# This path will be expanded relative to the project directory
chef.cookbooks_path = "cookbooks"
end
end
By default, Vagrant has an empty run list, or the list of recipes or roles for Chef to run. We’ve setup a vagrant_main cookbook above which we’ll make our entrypoint. The default recipe for this cookbook is shown below:
# vagrant_main cookbook
# This cookbook includes and sets up a server with apache, mysql,
# rails, and passenger.
#
require_recipe "apache2"
require_recipe "mysql"
require_recipe "rails"
require_recipe "passenger_apache2::mod_rails"
Then, we must tell Vagrant to use this cookbook:
Vagrant::Config.run do |config|
config.vm.provision :chef_solo do |chef|
chef.add_recipe("vagrant_main")
end
end
Every chef cookbook has access to the node variable which is a hash containing server-specific configuration options which can be used to control provisioning. By default, Vagrant JSON configuration looks like the following:
{
:instance_role => "vagrant",
:vagrant => {
:config => { ... }, # Full Vagrant config
}
}
This JSON configuration is specifically thought out such that the instance_role key could be used so that cookbooks could be shared between production and development, possibly tweaking paths or configuration based on the instance_role.
But sometimes, cookbooks need additional, custom JSON configuration. For this you can specify additional JSON data in the Vagrantfile:
Vagrant::Config.run do |config|
config.vm.provision :chef_solo do |chef|
chef.json = {
:load_limit => 42,
:chunky_bacon => true
})
end
end
Chef solo supports roles, which are specified via JSON files within a roles directory. Similar to the cookbooks path, a roles path can be specified to a directory containing these role files, and these roles can then be used by the chef solo run list. An example of configuring roles is shown below:
Vagrant::Config.run do |config|
config.vm.provision :chef_solo do |chef|
# The roles path will be expanded relative to the project directory
chef.roles_path = "roles"
chef.add_role("web")
end
end
In order to run chef, Vagrant has to mount the specified cookbooks directory as a shared folder on the virtual machine. By default, this is set to be /tmp/vagrant-chef, and this should be fine for most users. But in the case that you need to customize the location, you can do so in the Vagrantfile:
Vagrant::Config.run do |config|
config.vm.provision :chef_solo do |chef|
chef.provisioning_path = "/tmp/vagrant-chef"
end
end
This folder is created for provisioning purposes and destroyed once provisioning is complete.
By calling config.vm.provision with :chef_solo, chef solo based provisioning will be enabled and ran during a VM setup. If you are building a VM from scratch, run vagrant up and provisioning will automatically occur. If you already have a running VM and don’t want to rebuild everything from scratch, run vagrant reload and it will restart the VM, without completely destroying the environment first, allowing the import step to be skipped.