Understanding the Critical Difference Between include vs require in Puppet: Avoiding Circular Dependency Issues


2 views

In Puppet, both include and require are used to declare class relationships, but they establish fundamentally different dependency chains:

# Safe declaration (soft dependency)
class webserver {
  include apache
}

# Strict declaration (enforced ordering)
class database {
  require mysql
}

The require keyword creates an immediate hard dependency that Puppet must resolve during catalog compilation. When two classes require each other:

class A {
  require B
}

class B {
  require A  # This creates a loop
}

Whereas include creates a soft dependency that allows the catalog to compile first, then establishes relationships during application:

class A {
  include B  # No immediate validation
}

class B {
  include A  # Safe reciprocal inclusion
}

Follow these guidelines for optimal class declarations:

  • Use include for most class relationships (90% of cases)
  • Only use require when strict ordering is absolutely necessary
  • Combine with resource-level ordering (before, require) when needed
# Preferred pattern
class base {
  include networking
  include security
  
  package { 'essential-tools':
    ensure => present,
    before => Class['networking']
  }
}

For complex dependencies, consider these more maintainable approaches:

# Class chaining
class{'apache': } -> class{'mysql': }

# Resource collectors
Class['apache'] -> Class['mysql']

# Hiera-based declaration
lookup('classes', Array[String], 'unique', []).include

When encountering dependency loops, use these Puppet commands:

puppet master --compile node_name --debug
puppet catalog find node_name --render-as json

In Puppet, both include and require are used to declare classes, but they handle dependencies in fundamentally different ways:

# include example - non-strict declaration
class webserver {
  include apache
}

# require example - strict dependency
class database {
  require mysql
}

The critical behavior difference appears when classes reference each other:

# This creates circular dependency (BAD)
class A {
  require B
}

class B {
  require A
}

# This works fine (GOOD)
class A {
  include B
}

class B {
  include A
}

Puppet's catalog builder processes declarations differently:

  • include: Adds the class to catalog without immediate evaluation
  • require: Forces immediate evaluation and creates hard dependency
# Preferred pattern for most cases
class application {
  include prerequisites  # Soft dependency
  require configuration # Hard dependency when absolutely needed
}

# Good alternative using contain
class secure_app {
  include firewall
  contain firewall  # Creates proper scope boundary
}

For complex scenarios, consider these approaches:

# Parameterized class pattern
class app_server(
  Boolean $require_db = true
) {
  if $require_db {
    require database
  } else {
    include database
  }
}

# Class inheritance alternative
class base {
  include common_packages
}

class app inherits base {
  # automatically includes common_packages
}