This guide series will be about setting up with configuration management and TDD.

Setting up a delivery system between Chef Server on a workstation for staging, testing and even deploying to a remote production server!

As I only have one production server and I wanted configuration management and ease of deployment, I picked Chef. As I update the server irregularly, I wanted to push changes to it from my work station with Chef Server. I find this set up great as I could create more production servers and then move the Chef Server to its own server.

I choose not to develop with Chef-Solo as it has some limitations I came across. For example, Knife only works with Chef-Server and Chef-Server cannot resolve cookbooks with version numbers that’s downloaded by Berkshelf. It is also easier to test with kitchen test!

Note: My workstation is Linux Mint 18 Cinnamon 64-bit.

Lets get started setting up necessary files for Chef Server, create a new folder with chef and start git

$ mkdir ~/sanctionedlist/chef && cd ~/sanctionedlist/chef && git init
$ wget

Source: the sha256sum from Source then install

$ sha256sum chefdk_1.2.22-1_amd64.deb
518ecf308764c08a647ddabc6511af231affd2bf3e6526e60ef581926c8e7105  chefdk_1.2.22-1_amd64.deb

$ sudo dpkg -i chefdk_1.2.22-1_amd64.deb

Is Chef installed?

$ chef -v
Chef Development Kit Version: 1.2.22
chef-client version: 12.18.31
delivery version: master (0b746cafed65a9ea1a79de3cc546e7922de9187c)
berks version: 2017-02-04T13:37:27.680338 19865] 2017-02-04T13:37:27.680435 19865] 2017-02-04T13:37:27.680514 19865] 2017-02-04T13:37:27.680581 19865] 2017-02-04T13:37:27.702149 19865] 2017-02-04T13:37:27.702240 19865] 5.6.0
kitchen version: 1.15.0

Grab virtualbox from Oracle. Needed for Vagrant

$ wget

Source: the sha256sum from Source then install

$ sha256sum virtualbox-5.1_5.1.14-112924~Ubuntu~xenial_amd64.deb
61bd2e0b702e80c6f9b61e900a7cd6b773ca03cdf1de1439241ec126a518fdce  virtualbox-5.1_5.1.14-112924~Ubuntu~xenial_amd64.deb

$ sudo dpkg -i virtualbox-5.1_5.1.14-112924~Ubuntu~xenial_amd64.deb

If you run into dependency issues, it’s due to dpkg not installing dependencies. Run this to install dependencies:

$ sudo apt-get install -f

Is Virtualbox installed?

$ vboxmanage –version

Lets grab vagrant

$ wget

Source: the sha256sum from Source then install

$ sha256sum vagrant_1.9.1_x86_64.deb
d006d6227e049725b64d8ba3967f0c82460a403ff40230515c93134d58723150  vagrant_1.9.1_x86_64.deb

$ sudo dpkg -i vagrant_1.9.1_x86_64.deb

Check Vagrant

$ vagrant -v
Vagrant 1.9.1

Lets add a box to Vagrant

$ vagrant box add bento/ubuntu-16.04 --provider=virtualbox
  ==> box: loading metadata for box 'bento/ubuntu-16.04'
  ==> box: URL:
  ==> box: adding box 'bento/ubuntu-16.04' (v2.3.1) for provider: virtualbox
  ==> box: Downloading:
  ==> box: box download is resuming from prior download progress
  ==> box: successfully added box 'bento/ubuntu-16.04' (v2.3.1) for 'virtualbox'!

Note: The bento part refers to Bento, a Chef project that provides Vagrant boxes for many common platforms.

Lets initialise it with a Vagrantfile

$ vagrant init bento/ubuntu-16.04

Test the instance

$ vagrant up
  ==> default: machine booted and ready!
  ==> default: checking for guest additions in vm...
      default: The guest additions on this VM do not match the installed version of
      default: VirtualBox! In most cases this is fine, but in rare cases it can
      default: prevent things such as shared folders from working properly. If you see
      default: shared folder errors, please make sure the guest additions within the
      default: virtual machine match the version of VirtualBox you have installed on
      default: your host and reload your VM.
      default: Guest Additions Version: 5.0.26
      default: VirtualBox Version: 5.1
==> default: mounting shared folders...
      default: /vagrant => /home/andreas/sanctionedlist

It works! Lets close it and prepare for next step.

$ vagrant halt

Installing Chef Server

We finally have Chef! But the above is a lot of commands and it’s prone to human error. Why not start managing our workstation with Chef? Lets write a cookbook to do it for us.

We’re going to bootstrap our Chef Server with Chef Solo. Create Chef Solo configuration file

# ~/sanctionedlist/chef/solo.rb
root = File.absolute_path(File.dirname(__FILE__))

file_cache_path "/tmp/chef-solo/cache"
cookbook_path [root + "/cookbooks"]

Create the boostrap cookbook

$ mkdir cookbooks

$ chef generate cookbook cookbooks/bootstrap

$ ls -a cookbooks/bootstrap
.   Berksfile   .delivery   .kitchen.yml  spec
..  chefignore  .gitignore  metadata.rb   recipes    test

Notice how .gitignore was created with some values added for us? But this is not a repository yet. Lets create one as it’s good practice to have a repository for each cookbook.

$ git init
$ git add .
$ git commit -m "Initial bootstrap recipe"

Note: I choose the separation of chef repo and cookbook repo path over the monolithic cookbooks repo. Initially, starting with a complete chef repo would be easy. But later, cookbooks gets harder to maintain and you don’t need a lot of community cookbooks. More information on this path:

Set up the bootstrap recipe as below. Chef Server download page.

# ~/sanctionedlist/chef/cookbooks/bootstrap/recipes/default.rb

# This is largely copied from the official guide to installing chef server
# as at 11th Feb 2017

tmp_file = node['bootstrap']['tmp_dir'] + node['bootstrap']['file']
remote_file tmp_file do
  source node['bootstrap']['source_file']
  mode 644
  checksum node['bootstrap']['checksum'] # PUT THE SHA256 CHECKSUM HERE
  show_progress true

# After a few minutes, the Chef server will be installed.
dpkg_package 'chef-server' do
  source tmp_file
  action :install

# Run the following to start all of the services.
execute 'Start_Chef_Server' do
  command 'chef-server-ctl reconfigure'

# Run the following command to create an administrator.
user_name = node['bootstrap']['user_name']
first_name = node['bootstrap']['first_name']
last_name = node['bootstrap']['last_name']
email = node['bootstrap']['email']
password = node['bootstrap']['password']
user_filename = node['bootstrap']['user_filename']

execute 'Create_User' do
  command "chef-server-ctl user-create #{user_name} #{first_name} #{last_name}
#{email} '#{password}' --filename #{user_filename}"

# Run the following command to create an organization
short_name = node['bootstrap']['short_name']
full_organization_name = node['bootstrap']['full_organization_name']
organisation_filename = node['bootstrap']['organisation_filename']

execute 'Create_Organisation' do
  command "chef-server-ctl org-create #{short_name} '#{full_organization_name}'
--association_user #{user_name} --filename #{organisation_filename}"

Now lets add the attributes!

$ chef generate attribute default
# ~/sanctionedlist/chef/cookbooks/bootstrap/attributes/default.rb
default['bootstrap']['tmp_dir'] = '/tmp/'
source = ''
default['bootstrap']['source_file'] = source
default['bootstrap']['file'] = File.basename(source)
default['bootstrap']['checksum'] =

default['bootstrap']['user_name'] = 'andreas'
default['bootstrap']['first_name'] = 'Andreas'
default['bootstrap']['last_name'] = 'Markauskas'
default['bootstrap']['email'] = ''
default['bootstrap']['password'] = 'password1'
default['bootstrap']['user_filename'] = '~/sanctionedlist/chef/andreas.pem'
default['bootstrap']['short_name'] = 'sanctionedlist'
default['bootstrap']['full_organization_name'] = 'SanctionedList'
default['bootstrap']['organisation_filename'] =

Lets run chef solo! This is telling our chef solo to run bootstrap’s default recipe.

$ sudo chef-solo -c ./solo.rb  -o bootstrap::default

Clean up your code a bit, save the new changes and lets move to next step.

$ rubocop
$ git add .
$ git commit -m “Finished bootstrap recipe”

Note: I tried the official chef-server cookbook but it gave me an error “No package found for ‘chef-server’ with version ‘latest’ for current platform in ‘stable’ channel.” I could not resolve this as packages didn’t exist for my OS.Try going to your Chef Server link: https://andreas/organizations/sanctionedlist (Replace http://andreas with your hostname)No addons installed yet. Lets do that next. Grab Chef Manage add-on.

Note: I skipped chef-server-ctl install chef-manage due to a common Github issue #118

Unpackage and run this command. The –accept-license flag automatically accepts the licence.

$ chef-manage-ctl reconfigure --accept-license

Great, you should see the add-ons now on your Chef Server. https://andreas/organizations/sanctionedlist

You’ll see that there’s no cookbooks. Lets prepare a way to upload cookbooks now and later with Berkshelf! Create the berks configuration file:

# ~/.berkshelf/config.json
"chef": {
        "chef_server_url": "https://andreas/organizations/sanctionedlist",
        "validation_client_name": "sanctionedlist",
        "validation_key_path": "/home/andreas/sanctionedlist/chef/org-validation.pem",
        "client_key": "/home/andreas/sanctionedlist/chef/andreas.pem",
        "node_name": "andreas"
"cookbook": {
        "copyright": "YOUR_NAME",
        "email": "YOUR_EMAIL",
        "license": "reserved"
"allowed_licenses": [],
    "raise_license_exception": false,
    "vagrant": {
        "vm": {
            "box": "Berkshelf-CentOS-6.3-x86_64-minimal",
            "box_url": "",
            "forward_port": {},
            "network": {
                "bridged": false,
                "hostonly": ""
"provision": "chef_zero"

Then run:

$ berks upload
Uploaded bootstrap (0.1.0) to: 'https://andreas:443/organizations/sanctionedlist'

If you run into ssl errors like I did below:

ERROR -- : Ridley::Errors::ClientError: SSL_connect returned=1 errno=0 state=error: certificate verify failed

Do the following

$ export SSL_CERT_FILE='/home/andreas/sanctionedlist/chef/trusted_certs/andreas.crt'

Note: ‘~/sanctionedlist/chef/trusted_certs/andreas.crt’ doesn’t work. Use the full address!To make this export permanent, add to ~/.bash_profile. Then re-run berks upload.Alternatively, you can use the -ssl-verify flag.

$ berks upload --ssl-verify=false

Another issue I ran into:

Ridley::Errors::ClientError: hostname "Andreas" does not match the server certificate

This was a headscratcher until I realised that it was case sensitive and I used lower case hostname: https://andreas/organizations/sanctionedlist (It previously had Andreas as hostname)Congratulations, your Chef Server is armed with cookbooks. Lets try deploying to a Chef Client to our VM.Initialise the knife file:

# ~/sanctionedlist/chef/knife.rb
current_dir = File.dirname(__FILE__)
log_level                :info
log_location             STDOUT
node_name                "andreas"
client_key               "#{current_dir}/andreas.pem"
validation_key           "#{current_dir}/org-validation.pem"
chef_server_url          "https://andreas/organizations/sanctionedlist"
syntax_check_cache_path  "#{ENV['HOME']}/.chef/syntaxcache"
cookbook_path            ["#{current_dir}/cookbooks", "~/.berkshelf/cookbooks"]

The following items should be adjusted to suit your infrastructure:

  • node_name: This specifies the name that knife will use to connect to your Chef server. This should match your user name.
  • client_key: This should be the name and path to the user key that you copied over from the Chef server. We can use the #{current_dir} snippet to fill in the path if the key is in the same directory as the knife.rb file.
  • validation_client_name: This is the name of the validation client that knife will use to bootstrap new nodes. This will take the form of your organization short name, followed by -validator.
  • validation_key: Like the client_key, this includes the name and path to the validation key you copied from the Chef server. Again, you can use the #{current_dir} Ruby snippet to specify the current directory if the validation key is in the same directory as the knife.rb file.
  • chef_server_url: This is the URL where the Chef server can be reached. It should begin with https://, followed by your Chef server’s domain name or IP address. Afterwards, the path to your organization should be specified by appending /organizations/your_organization_name.

Now if you tried to run the chef server command towards vagrant, it would fail.

$ sudo knife bootstrap andreas -p 2222 --ssh-user vagrant -P vagrant -N test --sudo --node-ssl-verify-mode none
  ERROR: Connection refused connecting to https://andreas/organizations/sanctionedlist/nodes/test, retry 1/5

Of course, Vagrant can’t connect to your host desktop because Vagrant accessing http://andreas doesn’t equal Chef Server. Lets fix it by going into Vagrant and adding a quick fix to hostfile

$ vagrant ssh

$ sudo sh -c "echo ' andreas' >> /etc/hosts"

By default, Vagrant assigns the host ip address as You should see this ip address from the last login when you enter vagrant ssh. Lets try bootstrapping again

$ sudo knife bootstrap -p 2222 --ssh-user vagrant -P vagrant -N test --sudo --node-ssl-verify-mode none
Creating new client for test
Creating new node for test
Connecting to
[...] [2017-02-14T11:35:23+00:00] WARN: Node test has an empty run list Converging 0 resources Running handlers: Running handlers complete Chef Client finished, 0/0 resources updated in 01 seconds

Congratulations, you have bootstrapped a node from your desktop! Go to your browser and it will appear as an available node. https://andreas/organizations/sanctionedlist/nodes

Next post, Setting up testing with Chef Kitchen – Unit testing

2 thoughts on “

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s