Chef Infrastructure Fundamentals

My First Real Chef Cookbook

My first serious Chef cookbook was born out of necessity. I needed to standardize NGINX configurations across 30 web servers, each requiring slightly different virtual host configurations based on their role. Manual configuration was taking hours and introducing inconsistencies. I knew Chef could solve this, but I had to truly understand resources, recipes, and cookbooks to make it work.

After several iterations and some painful mistakes (like accidentally restarting nginx on all production servers simultaneously), I learned the patterns that make Chef powerful and reliable. This article shares those fundamental concepts that transformed me from a Chef novice to someone who could confidently automate complex infrastructure.

Understanding the Chef Resource Model

Resources are the foundation of Chef - they represent a desired state of a piece of infrastructure.

The Resource Anatomy

package 'nginx' do
  action :install
  version '1.18.0'
end

Breaking it down:

  • package - Resource type

  • 'nginx' - Resource name (what to manage)

  • action :install - What to do with it

  • version '1.18.0' - Resource properties

Why this matters: Chef doesn't run commands, it declares states. If nginx 1.18.0 is already installed, Chef does nothing. This idempotency is crucial for reliable automation.

Common Resources I Use Daily

File Resources

My lesson learned: Always use templates for configuration files that need variables. Static files are for truly static content only.

Package Resources

Platform abstraction: The same package resource works on Ubuntu (apt), CentOS (yum), and other platforms.

Service Resources

My pattern: Always use :enable to ensure services start on boot, not just :start.

Directory Resources

User and Group Resources

Real scenario: I use this for creating application-specific service accounts with restricted permissions.

Execute Resources

Critical lesson: Always use guards (only_if, not_if) with execute resources to maintain idempotency.

Building Recipes

Recipes are collections of resources that achieve a specific configuration goal.

My First Recipe: Web Server Setup

Why this works:

  1. Install package first

  2. Create necessary directories

  3. Deploy configuration

  4. Start service (notified if config changes)

Recipe Organization Patterns

Single Responsibility:

My default.rb pattern:

Working with Templates

Templates use ERB (Embedded Ruby) to create dynamic configuration files.

Basic Template

templates/default/nginx.conf.erb:

Using the template:

Advanced Template with Conditionals

templates/default/app-config.erb:

My real usage:

Understanding Attributes

Attributes provide data for recipes and templates.

Attribute Precedence

From lowest to highest priority:

  1. Default attributes (in cookbook)

  2. Environment attributes

  3. Role attributes

  4. Node attributes

  5. Override attributes

attributes/default.rb:

Accessing Attributes in Recipes

My Attribute Organization Pattern

Notification and Subscriptions

Resources can trigger other resources to take action.

Notifies Pattern

Timing options:

  • :delayed - Run at end of Chef run (default, safest)

  • :immediate - Run immediately

My rule: Use :delayed unless you need immediate action. It prevents cascading restarts.

Multiple Notifications

Real scenario: Test configuration before reloading to avoid breaking the service.

Subscribes Pattern

When I use subscribes: When the triggered resource is defined in a different recipe.

Cookbook Structure Best Practices

My Standard Cookbook Layout

metadata.rb - The Cookbook Manifest

Version management: I use semantic versioning and update with each change.

Guards and Conditional Execution

only_if and not_if

Platform-Specific Resources

My approach: Write platform-agnostic code when possible, use conditionals only when necessary.

Real-World Example: Database Server Cookbook

Let me share a complete cookbook I built for PostgreSQL:

recipes/default.rb:

recipes/install.rb:

recipes/configure.rb:

recipes/service.rb:

attributes/default.rb:

templates/default/postgresql.conf.erb:

Common Patterns I Use

Idempotent File Markers

Graceful Service Management

Looping Over Data

Testing Your Recipes

Using Test Kitchen

Writing Integration Tests

test/integration/default/default_test.rb:

Lessons Learned

1. Always Use Idempotent Resources

❌ execute 'restart nginx' - Runs every time βœ… Use notifications to restart only when needed

2. Test Configurations Before Applying

3. Use Templates for Configuration

  • Static files for truly static content

  • Templates for anything with variables

  • Never hardcode environment-specific values

4. Organize Attributes Clearly

5. Keep Recipes Focused

One recipe = one responsibility

What's Next?

You now understand: βœ… Chef resources and their properties βœ… Building recipes with proper structure βœ… Working with templates and attributes βœ… Notifications and service management βœ… Cookbook organization best practices βœ… Testing and validation patterns

Continue to Writing and Testing Cookbooks for advanced cookbook development patterns, or jump to Chef Server and Node Management to learn about scaling your Chef infrastructure.

The fundamentals you've learned here are the building blocks for all Chef automation. Master these patterns, and you'll be able to automate any infrastructure configuration with confidence.


Ready to level up your cookbook development? Continue to Writing and Testing Cookbooks

Last updated