A comprehensive comparison of Ansible and Puppet for configuration management, with real-world use cases and migration strategies.
After years of managing infrastructure with both Ansible and Puppet, I've learned that each tool excels in different scenarios. This guide will help you choose the right tool for your infrastructure needs and avoid common pitfalls.
Architecture Differences
Ansible: Agentless Simplicity
- Push-based model: Control node pushes configurations
- No agents required: Uses SSH/WinRM for communication
- YAML playbooks: Human-readable configuration
- Procedural approach: Step-by-step execution
Puppet: Agent-Based Control
- Pull-based model: Agents pull configurations
- Master-agent architecture: Centralized management
- Puppet DSL: Domain-specific language
- Declarative approach: Describe desired state
Getting Started Comparison
Ansible Quick Start
# inventory.yml
webservers:
hosts:
web1.example.com:
web2.example.com:
# playbook.yml
- name: Configure Web Servers
hosts: webservers
become: yes
tasks:
- name: Install Nginx
package:
name: nginx
state: present
- name: Start Nginx
service:
name: nginx
state: started
enabled: yes
- name: Deploy config
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: restart nginx
handlers:
- name: restart nginx
service:
name: nginx
state: restarted
Puppet Quick Start
# site.pp
node 'web1.example.com', 'web2.example.com' {
class { 'nginx':
ensure => present,
enable => true,
}
file { '/etc/nginx/nginx.conf':
ensure => file,
source => 'puppet:///modules/nginx/nginx.conf',
require => Package['nginx'],
notify => Service['nginx'],
}
service { 'nginx':
ensure => running,
enable => true,
require => Package['nginx'],
}
}
Use Case Analysis
When to Choose Ansible
Ideal for:
- Rapid deployment and provisioning
- Multi-cloud orchestration
- Ad-hoc tasks and one-time configurations
- Small to medium environments
- Teams with limited configuration management experience
Real-world example:
# Multi-cloud deployment
- name: Deploy to AWS
hosts: localhost
tasks:
- amazon.aws.ec2_instance:
name: "web-{{ item }}"
instance_type: t3.micro
image_id: ami-12345678
count: 1
loop: "{{ range(1, 4) | list }}"
- name: Deploy to Azure
hosts: localhost
tasks:
- azure.azcollection.azure_rm_virtualmachine:
name: "web-{{ item }}"
vm_size: Standard_B1s
image: UbuntuLTS
loop: "{{ range(1, 4) | list }}"
When to Choose Puppet
Ideal for:
- Large-scale enterprise environments
- Strict compliance requirements
- Complex dependency management
- Long-term configuration drift prevention
- Environments requiring detailed reporting
Real-world example:
# Compliance-focused configuration
class security::baseline {
# CIS Benchmark implementation
file { '/etc/ssh/sshd_config':
ensure => file,
owner => 'root',
group => 'root',
mode => '0600',
content => template('security/sshd_config.erb'),
}
augeas { 'kernel.parameters':
context => '/files/etc/sysctl.conf',
changes => [
'set net.ipv4.tcp_syncookies 1',
'set net.ipv4.conf.all.rp_filter 1',
'set net.ipv4.conf.default.rp_filter 1',
],
}
# Automatic compliance reporting
@@nagios_service { "compliance_${hostname}":
ensure => present,
host_name => $hostname,
service_description => 'Security Compliance',
check_command => 'check_compliance',
}
}
Performance and Scalability
Ansible Performance
# Optimize with strategies
- name: Parallel execution
hosts: all
strategy: free
tasks:
- name: Update packages
package:
name: "*"
state: latest
async: 3600
poll: 0
register: update_job
# Fact caching for performance
[defaults]
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_cache
fact_caching_timeout = 3600
Puppet Performance
# Puppet server tuning
# /etc/puppetlabs/puppetserver/conf.d/puppetserver.conf
jruby-puppet: {
max-active-instances: 4
max-requests-per-instance: 10000
}
# Client-side caching
[agent]
usecacheonfailure = true
report = true
runinterval = 30m
Learning Curve and Team Adoption
Ansible Learning Path
- Week 1: Basic playbooks and inventory
- Week 2: Variables, templates, and handlers
- Week 3: Roles and Galaxy
- Week 4: Advanced features (vault, AWX/Tower)
Puppet Learning Path
- Week 1: Resources and manifests
- Week 2: Classes and modules
- Week 3: Hiera and data separation
- Week 4: PuppetDB and reporting
- Week 5-6: Advanced patterns and Puppet Enterprise
Integration Capabilities
Ansible Integrations
# CI/CD Integration
- name: Jenkins Pipeline
hosts: localhost
tasks:
- uri:
url: "http://jenkins:8080/job/deploy/build"
method: POST
user: "{{ jenkins_user }}"
password: "{{ jenkins_token }}"
# Secret Management
- name: Retrieve from Vault
hashivault_read:
path: secret/data/app
key: database_password
register: db_password
Puppet Integrations
# Kubernetes Integration
kubernetes_deployment { 'frontend':
ensure => present,
metadata => {
namespace => 'production',
},
spec => {
replicas => 3,
selector => {
matchLabels => {
app => 'frontend',
},
},
},
}
# ServiceNow CMDB
node default {
$cmdb_data = lookup('cmdb::configuration', Hash, 'deep')
class { 'application':
config => $cmdb_data,
}
}
Cost Comparison
Ansible Costs
- Ansible Core: Free and open source
- Red Hat Ansible Automation Platform:
- Standard: ~$10,000/year (100 nodes)
- Premium: ~$14,000/year (100 nodes)
- Training: $3,000-5,000 per person
- Implementation: 2-4 weeks typical
Puppet Costs
- Puppet Open Source: Free
- Puppet Enterprise:
- Standard: ~$120/node/year
- Premium: ~$199/node/year
- Training: $3,500-6,000 per person
- Implementation: 4-8 weeks typical
Migration Strategies
From Puppet to Ansible
#!/usr/bin/env python3
# Convert Puppet manifests to Ansible playbooks
import yaml
import re
def convert_puppet_to_ansible(puppet_manifest):
playbook = {
'name': 'Converted from Puppet',
'hosts': 'all',
'tasks': []
}
# Parse package resources
packages = re.findall(r"package\s*{\s*'([^']+)'.*?ensure\s*=>\s*(\w+)",
puppet_manifest, re.DOTALL)
for pkg, state in packages:
playbook['tasks'].append({
'name': f'Install {pkg}',
'package': {
'name': pkg,
'state': 'present' if state == 'installed' else state
}
})
return yaml.dump([playbook])
From Ansible to Puppet
# Convert Ansible playbooks to Puppet manifests
require 'yaml'
def convert_ansible_to_puppet(playbook_path)
playbook = YAML.load_file(playbook_path)
manifest = ""
playbook['tasks'].each do |task|
if task['package']
manifest += <<-PUPPET
package { '#{task['package']['name']}':
ensure => #{task['package']['state']},
}
PUPPET
end
end
manifest
end
Testing and Validation
Ansible Testing
# Molecule testing
molecule:
- name: default
driver:
name: docker
platforms:
- name: ubuntu
image: ubuntu:20.04
verifier:
name: ansible
# Ansible-lint
ansible-lint playbook.yml --exclude .github
Puppet Testing
# RSpec testing
require 'spec_helper'
describe 'nginx' do
context 'with default parameters' do
it { is_expected.to compile }
it { is_expected.to contain_package('nginx') }
it { is_expected.to contain_service('nginx').with_ensure('running') }
end
end
Common Pitfalls and Solutions
Ansible Pitfalls
- Idempotency issues: Always use modules instead of shell commands
- Variable precedence confusion: Document your variable hierarchy
- Slow execution: Use mitogen or pipelining for speed
Puppet Pitfalls
- Catalog compilation errors: Test in development first
- Certificate management: Automate certificate signing
- Resource ordering: Use proper relationships (require, notify)
Making the Decision
Choose Ansible if you:
- Need quick implementation
- Prefer agentless architecture
- Have dynamic, cloud-based infrastructure
- Want easier learning curve
- Focus on orchestration over configuration
Choose Puppet if you:
- Manage large, stable infrastructure
- Require detailed compliance reporting
- Need strong Windows support
- Want declarative configuration
- Have dedicated DevOps team
Conclusion
Both Ansible and Puppet are excellent tools that solve configuration management differently. Ansible's simplicity and flexibility make it perfect for modern, dynamic infrastructures. Puppet's robustness and reporting capabilities excel in large, compliance-focused enterprises.
Many organizations successfully use both: Ansible for orchestration and deployment, Puppet for configuration enforcement and compliance. Choose based on your specific needs, team skills, and infrastructure requirements.
Share this article
David Childs
Consulting Systems Engineer with over 10 years of experience building scalable infrastructure and helping organizations optimize their technology stack.