Knife is the command-line tool that connects your Chef Workstation to the Chef Infra Server. It handles everything from uploading cookbooks and managing node run lists to searching indexed data and working with data bags. If you manage infrastructure with Chef, knife is the tool you use daily.
This guide walks through configuring knife on your workstation, verifying server connectivity, creating and uploading cookbooks, running recipes on nodes, and managing data bags. These steps apply to any supported Linux distribution with Chef Workstation installed.
Prerequisites
Before starting, make sure you have the following in place:
- A running Chef Infra Server (self-hosted or managed)
- Chef Workstation installed on your local machine – this provides the
knifeandchefcommands - Your user RSA private key (
.pemfile) downloaded from the Chef Server - The Chef Server organization name and URL
- At least one target node (server) with SSH access for running recipes
Step 1: Configure knife.rb
Knife reads its configuration from a file called knife.rb inside a .chef directory. Start by generating a Chef repo and creating the configuration file.
Generate the Chef repository structure on your workstation:
chef generate repo chef-repo
cd chef-repo
mkdir -p .chef
Copy your user private key into the .chef directory:
cp ~/your-username.pem ~/chef-repo/.chef/
Create the knife configuration file:
vi ~/chef-repo/.chef/knife.rb
Add the following configuration, replacing the values to match your environment:
current_dir = File.dirname(__FILE__)
log_level :info
log_location STDOUT
node_name 'admin'
client_key "#{current_dir}/admin.pem"
chef_server_url 'https://chef-server.example.com/organizations/myorg'
cookbook_path ["#{current_dir}/../cookbooks"]
Here is what each setting does:
node_name– your Chef Server usernameclient_key– path to your RSA private key filechef_server_url– full URL to your Chef Server organizationcookbook_path– local directory where knife looks for cookbooks
Prevent the .chef directory from being committed to version control by adding it to .gitignore:
echo '.chef' >> ~/chef-repo/.gitignore
Step 2: Verify Knife Connectivity to Chef Server
Before doing anything else, confirm that knife can communicate with the Chef Server. Run the SSL certificate check first:
knife ssl check
A successful check confirms the SSL certificate is valid and trusted:
Connecting to host chef-server.example.com:443
Successfully verified certificates from 'chef-server.example.com'
If you get a certificate error, fetch the server’s SSL certificate to your trusted certificates directory:
knife ssl fetch
Next, verify authentication by listing registered clients on the Chef Server:
knife client list
You should see the organization validator client and any previously registered nodes:
myorg-validator
If you get an authentication error (401), double-check that node_name in knife.rb matches your Chef Server username and the .pem file is correct.
Step 3: Create a Cookbook
Cookbooks are the fundamental unit of configuration in Chef. Each cookbook contains recipes, templates, files, and metadata that define how a piece of infrastructure should be configured. Generate a new cookbook using the chef generate command:
cd ~/chef-repo/cookbooks
chef generate cookbook my_webserver
This creates a complete cookbook directory structure:
my_webserver/
├── CHANGELOG.md
├── LICENSE
├── Policyfile.rb
├── README.md
├── chefignore
├── compliance
│ └── ...
├── kitchen.yml
├── metadata.rb
├── recipes
│ └── default.rb
└── test
└── integration
└── default
└── default_test.rb
The key files are metadata.rb (cookbook name, version, dependencies) and recipes/default.rb (the default recipe that runs when the cookbook is applied). Review the metadata file to confirm the cookbook name and version:
cat ~/chef-repo/cookbooks/my_webserver/metadata.rb
The output shows the generated metadata with version 0.1.0:
name 'my_webserver'
maintainer 'The Authors'
maintainer_email '[email protected]'
license 'All Rights Reserved'
description 'Installs/Configures my_webserver'
version '0.1.0'
chef_version '>= 16.0'
Step 4: Write a Recipe
Recipes define the resources (packages, services, files, templates) that Chef manages on a node. Open the default recipe file:
vi ~/chef-repo/cookbooks/my_webserver/recipes/default.rb
Add the following recipe that installs Apache HTTP Server, creates a custom index page, and starts the service:
# Install Apache web server
package 'httpd' do
action :install
end
# Create a custom index page
file '/var/www/html/index.html' do
content 'Managed by Chef
'
mode '0644'
owner 'root'
group 'root'
action :create
end
# Enable and start Apache
service 'httpd' do
action [:enable, :start]
end
This recipe uses three resource types: package to install software, file to manage file content and permissions, and service to control the daemon. Chef processes resources in the order they appear, so the package installs before the service starts.
For Ubuntu/Debian nodes, replace httpd with apache2 in both the package and service resources.
Step 5: Upload Cookbook to Chef Server
Once the recipe is ready, upload the cookbook to the Chef Server so it becomes available for nodes. Run the upload command from your chef-repo directory:
knife cookbook upload my_webserver
A successful upload confirms the cookbook name and version:
Uploading my_webserver [0.1.0]
Uploaded 1 cookbook.
Verify the cookbook is on the server by listing all uploaded cookbooks:
knife cookbook list
The output shows all cookbooks with their current versions:
my_webserver 0.1.0
Step 6: Add Cookbook to Node Run List
A node’s run list determines which recipes Chef applies during a converge. Before adding a cookbook to the run list, bootstrap the node if it has not been registered with the Chef Server yet:
knife bootstrap 10.0.1.50 --ssh-user root --node-name web01 --run-list 'recipe[my_webserver]'
This command installs chef-client on the target node, registers it with the Chef Server, and sets its initial run list in a single step. If the node is already bootstrapped, add the cookbook to its run list with:
knife node run_list add web01 'recipe[my_webserver]'
Confirm the run list was updated correctly:
knife node show web01
The node details include the current run list and platform information:
Node Name: web01
Environment: _default
FQDN: web01.example.com
IP: 10.0.1.50
Run List: recipe[my_webserver]
Roles:
Recipes: my_webserver, my_webserver::default
Platform: rocky 10.0
Step 7: Run chef-client on the Node
With the run list configured, trigger a Chef converge on the node. You can run chef-client directly over SSH:
knife ssh 'name:web01' 'sudo chef-client' --ssh-user root
Chef connects to the server, downloads the cookbooks in the run list, and applies each recipe. A successful run shows the resources that were converged:
web01 Starting Chef Infra Client, version 18.5.0
web01 resolving cookbooks for run list: ["my_webserver"]
web01 Synchronizing Cookbooks:
web01 - my_webserver (0.1.0)
web01 Installing Cookbook Gems:
web01 Compiling Cookbooks...
web01 Converging 3 resources
web01 Recipe: my_webserver::default
web01 * yum_package[httpd] action install
web01 - install version 2.4.62-1.el10 of package httpd
web01 * file[/var/www/html/index.html] action create
web01 - create new file /var/www/html/index.html
web01 * service[httpd] action enable
web01 - enable service service[httpd]
web01 * service[httpd] action start
web01 - start service service[httpd]
web01
web01 Running handlers:
web01 Running handlers complete
web01 Chef Infra Client finished, 4/4 resources updated in 15 seconds
Alternatively, SSH into the node and run chef-client directly:
ssh [email protected] "chef-client"
After the run completes, verify the web server is responding:
curl -s http://10.0.1.50
You should see the custom page content confirming the recipe was applied:
<html><body><h1>Managed by Chef</h1></body></html>
Step 8: Manage Data Bags
Data bags store global JSON data that any cookbook can access. They are useful for storing configuration values like user accounts, API keys, or application settings that should not be hardcoded into recipes. Refer to the official Chef data bags documentation for advanced use cases.
Create a data bag called app_config:
knife data bag create app_config
The server confirms the data bag was created:
Created data_bag[app_config]
Create a JSON file for a data bag item. This example stores database connection details:
vi ~/chef-repo/data_bags/app_config/database.json
Add the following JSON content:
{
"id": "database",
"host": "db01.example.com",
"port": 5432,
"name": "app_production",
"user": "app_user"
}
Upload the data bag item to the Chef Server:
knife data bag from file app_config ~/chef-repo/data_bags/app_config/database.json
The upload confirms the item was stored:
Updated data_bag_item[app_config::database]
To read a data bag item in a recipe, use the data_bag_item method:
db_config = data_bag_item('app_config', 'database')
db_host = db_config['host']
db_port = db_config['port']
List all data bags and their items to verify everything is stored correctly:
knife data bag list
This returns all data bags on the server:
app_config
View items inside a specific data bag:
knife data bag show app_config database
The output displays the stored JSON values:
host: db01.example.com
id: database
name: app_production
port: 5432
user: app_user
For sensitive data like passwords, use encrypted data bags with knife data bag create --secret-file to protect values at rest on the Chef Server.
Step 9: Knife Commands Reference
The table below covers the most commonly used knife commands for day-to-day Chef operations.
| Command | Description |
|---|---|
knife ssl check | Verify SSL connectivity to Chef Server |
knife ssl fetch | Download and trust the Chef Server SSL certificate |
knife client list | List all registered API clients |
knife node list | List all registered nodes |
knife node show NODE | Display node details, run list, and attributes |
knife node run_list add NODE 'recipe[COOKBOOK]' | Add a recipe to a node’s run list |
knife node run_list remove NODE 'recipe[COOKBOOK]' | Remove a recipe from a node’s run list |
knife cookbook upload COOKBOOK | Upload a cookbook to the Chef Server |
knife cookbook list | List all cookbooks on the Chef Server |
knife cookbook delete COOKBOOK | Delete a cookbook from the Chef Server |
knife bootstrap HOST --node-name NAME | Install chef-client and register a new node |
knife ssh 'name:NODE' 'sudo chef-client' | Run chef-client remotely on a node |
knife data bag create BAG | Create a new data bag |
knife data bag from file BAG FILE | Upload a data bag item from a JSON file |
knife data bag show BAG ITEM | Display a data bag item’s contents |
knife search node 'platform:rocky' | Search nodes by attribute |
knife environment list | List all Chef environments |
knife role list | List all defined roles |
knife status | Show last check-in time for all nodes |
Conclusion
You now have a working knife configuration connected to your Chef Server, a cookbook uploaded and applied to a node, and data bags set up for shared configuration. This covers the core workflow you will repeat for every piece of infrastructure managed by Chef.
For production environments, store your cookbooks in Git and use a CI/CD pipeline to test and upload them. Keep sensitive data in encrypted data bags, rotate your .pem keys periodically, and set up chef-client to run on a scheduled interval (every 15-30 minutes) so nodes stay in the desired state automatically.