Fri, December 5, 2025 · 3 min read

Setting up chef dev environment

Setting up chef dev environment

Building a Practical Chef Lab on KVM and Vagrant

Most configuration-management tutorials assume you are running VirtualBox on a laptop. That works until you start dealing with real workloads or anything that resembles datacenter networking. For realistic testing, KVM on a Linux host behaves much closer to production. Combine it with Vagrant and you get reproducible provisioning without babysitting virtual machine definitions.

This write-up walks through setting up a Chef Server, a Workstation, and multiple Nodes using KVM, Vagrant, and libvirt.

Preparing the KVM Host

Install KVM and libvirt:

dnf install -y qemu-kvm libvirt libvirt-daemon libvirt-daemon-driver-qemu virt-install bridge-utils
systemctl enable --now libvirtd

Verify virtualization support:

egrep -c '(vmx|svm)' /proc/cpuinfo

Example isolated network definition:

<network>
<name>chef-net</name>
<bridge name='virbr50'/>
<ip address='192.168.56.1' netmask='255.255.255.0'/>
</network>

Activate it:

virsh net-define chef-net.xml
virsh net-start chef-net
virsh net-autostart chef-net

Installing Vagrant with Libvirt

Install Vagrant and the libvirt provider:

dnf install -y vagrant
vagrant plugin install vagrant-libvirt

Verify detection:

vagrant plugin list
vagrant up --provider=libvirt

Building a Reusable Base Image

A minimal Rocky Linux image built using Packer and QEMU provides consistency across all Chef nodes.

After building the VM, export it as a Vagrant box:

vagrant package --base rocky8-kvm --output rocky8-chef.box
vagrant box add rocky8-chef.box --name rocky8-chef

Controlling the base image avoids dependency drift and ensures reproducibility.

Directory Layout

A clean layout helps as the lab grows:

chef-lab/
Vagrantfile
cookbooks/
roles/
environments/
data_bags/
chef_repo/

Vagrantfile for the Chef Lab

Vagrant.configure("2") do |config|
  config.vm.box = "rocky8-chef"
 
  nodes = {
    "chef-server" => "192.168.56.10",
    "workstation" => "192.168.56.20",
    "node01" => "192.168.56.21",
    "node02" => "192.168.56.22"
  }
 
  nodes.each do |name, ip|
    config.vm.define name do |node|
      node.vm.hostname = name
      node.vm.network "private_network", ip: ip
 
      node.vm.provider :libvirt do |lv|
        lv.cpus = 2
        lv.memory = 2048
      end
    end
  end
end

Installing the Chef Server

Install Chef Server:

dnf install -y chef-server-core
chef-server-ctl reconfigure

Create the first user and organization:

chef-server-ctl user-create admin Admin User admin@example.com 'Password123' --filename admin.pem
chef-server-ctl org-create homelab "HomeLab Org" --association_user admin --filename homelab-validator.pem

Preparing the Chef Workstation

Install Chef Workstation:

dnf install -y chef-workstation

Copy keys from the server:

scp admin.pem homelab-validator.pem workstation:/home/vagrant/chef-repo/.chef/

Configure Knife:

knife configure

Test communication:

knife user list
knife node list

Bootstrapping Nodes

Bootstrap each node from the workstation:

knife bootstrap 192.168.56.21 --ssh-user vagrant --sudo --node-name node01
knife bootstrap 192.168.56.22 --ssh-user vagrant --sudo --node-name node02

Nodes should appear in:

knife node list

Managing Cookbooks

Generate a new cookbook:

chef generate cookbook apache_demo

Upload it:

knife cookbook upload apache_demo

Add it to a node’s runlist:

knife node run_list add node01 "recipe[apache_demo]"

Trigger a run:

sudo chef-client

Rebuilding the Lab

Destroy the environment:

vagrant destroy -f

Recreate it:

vagrant up

This guarantees a fresh environment for every test cycle.

Closing Notes

A Chef lab built on KVM and Vagrant mirrors real infrastructure far better than desktop hypervisors. You get proper virtio networking, efficient CPU virtualization, and the ability to scale without friction. This setup provides a reliable, repeatable environment for experimenting with Chef cookbooks, policyfiles, CI pipelines, and infrastructure automation concepts.