Home DevOps Ansible vs Chef vs Puppet vs SaltStack: Choose the Right Config Tool for Production
Intermediate 6 min · July 11, 2026

Ansible vs Chef vs Puppet vs SaltStack: Choose the Right Config Tool for Production

Ansible vs Chef vs Puppet vs SaltStack: Compare architecture, performance, and failure modes.

N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Drawn from code that ran under real load.

Follow
Production
production tested
July 11, 2026
last updated
1,727
articles · all by Naren
Before you start⏱ 30 min
  • Basic familiarity with configuration management concepts. No prior experience with any specific tool required.
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer

Choose Ansible for simplicity and agentless SSH-based management. Choose Chef for complex Ruby-driven automation. Choose Puppet for large-scale declarative state management. Choose SaltStack for high-speed event-driven orchestration.

✦ Definition~90s read
What is Ansible vs Chef vs Puppet vs SaltStack?

Ansible, Chef, Puppet, and SaltStack are infrastructure-as-code tools that automate server configuration, application deployment, and orchestration. Each takes a different approach to agent vs agentless, push vs pull, and language (YAML vs Ruby vs DSL).

Think of these tools as different ways to give instructions to a team of sysadmins.
Plain-English First

Think of these tools as different ways to give instructions to a team of sysadmins. Ansible is like giving a written checklist that each admin reads and follows on the spot — no prep needed. Chef is like writing a full recipe book that each admin installs and follows step-by-step. Puppet is like setting up a rulebook that admins check every few minutes to keep things consistent. SaltStack is like having a walkie-talkie to shout commands instantly to everyone at once.

⚙ Browser compatibility
Latest versions — ✓ supported
ChromeFirefoxSafariEdge

Most teams pick a config management tool based on hype, not hard production data. I've seen a 500-node cluster melt down because the team chose Puppet for its 'maturity' but didn't understand its pull-model latency. Another team spent six months fighting Chef's Ruby DSL when all they needed was a simple playbook. The wrong choice costs you not just time, but outages.

This article gives you a decision framework based on real failure modes: agent overhead, push vs pull latency, language complexity, and scaling ceilings. You'll learn exactly which tool fits your team size, infrastructure scale, and operational style.

By the end, you'll be able to explain to your CTO why Ansible is the right call for a 50-node startup, but why a 2000-node bank should look at SaltStack or Puppet. You'll also know the exact gotchas that burn people in production — like Chef's search performance or Ansible's SSH fork bomb.

Why Architecture Matters More Than Features

Before you compare playbooks vs recipes, understand the fundamental architectural split: push vs pull and agent vs agentless. This single decision determines your latency, scalability, and failure modes.

Ansible is agentless push: it SSHes into nodes and runs modules. No agent to install, but every run is a fresh SSH connection. For 50 nodes, it's fine. For 5000, SSH multiplexing still creates a thundering herd on the control node.

Chef and Puppet are pull-based with agents. The agent runs on each node, polls a central server, and applies the desired state. This scales because the server doesn't initiate connections. But it introduces latency: a node might be misconfigured for up to 30 minutes before the next pull.

SaltStack is hybrid: it uses ZeroMQ for high-speed push but also supports pull via minions. It's the fastest for ad-hoc commands but adds a message queue dependency.

Here's the production truth: if you need instant remediation (e.g., block an IP across all nodes), push wins. If you need eventual consistency without a central bottleneck, pull wins. Choose based on your latency SLA, not your feature checklist.

ansible_push_vs_pull.devopsDEVOPS
1
2
3
4
5
6
7
8
9
10
11
12
13
# io.thecodeforge — DevOps tutorial

# Ansible: agentless push via SSH
ansible all -i inventory.ini -m ping  # Single SSH connection per node

# Chef: pull-based agent runs every 30 min
chef-client --runlist 'recipe[my_cookbook::default]'  # Runs locally, pulls from server

# SaltStack: push via ZeroMQ
salt '*' test.ping  # Sub-second, even on 1000 nodes

# Puppet: pull-based agent
puppet agent --test  # Connects to Puppet master, fetches catalog
Output
Each command returns success/failure per node. Ansible shows SSH connection time (0.5-2s per node). SaltStack returns in <100ms for 1000 nodes.
Production Trap: SSH Fork Bomb
Ansible's default forks=5 serializes SSH connections. Bump it to 20-50 for large fleets, but watch for SSH connection limits on the control node. I've seen Ansible crash with 'Too many open files' when forks=100 on a 2000-node run.

Language and Learning Curve: YAML vs Ruby vs DSL

The language barrier is the #1 reason teams abandon a config tool. Chef uses Ruby — full Ruby, with all its metaprogramming power and all its debugging pain. Puppet uses its own DSL, which is simpler but limited. Ansible uses YAML — declarative, no logic, easy to read. SaltStack uses YAML with Jinja2 templating, plus Python for custom modules.

Here's the real-world trade-off: Chef's Ruby lets you write complex logic (loops, conditionals, data transformations) natively. But that power means your cookbooks can become unreadable spaghetti. I've seen a 400-line recipe that could have been 20 lines of Ansible YAML.

Ansible's YAML is deliberately limited. You can't write a for loop in pure Ansible — you use Jinja2 or lookup plugins. That's a feature, not a bug: it forces simplicity. But when you need dynamic behavior (e.g., iterate over a list of users with different SSH keys), you'll hit a wall.

SaltStack strikes a good balance: YAML for state definitions, Python for custom modules. Your team can write simple states without coding, and drop into Python when needed.

Puppet's DSL is Ruby-inspired but not Ruby. It's easy to learn but frustrating when you need to do something the DSL doesn't support — then you write custom facts or functions in Ruby, which feels like a hack.

Bottom line: if your team is strong in Ruby, Chef is powerful. If you want maximum simplicity, Ansible. If you want flexibility without full Ruby, SaltStack.

language_comparison.devopsDEVOPS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# io.thecodeforge — DevOps tutorial

# Ansible: YAML, no logic
- name: Install packages
  apt:
    name: "{{ packages }}"
  vars:
    packages:
      - nginx
      - git

# Chef: Ruby, full logic
package ['nginx', 'git'] do
  action :install
end

# SaltStack: YAML + Jinja2
install_packages:
  pkg.installed:
    - pkgs:
      - nginx
      - git

# Puppet: DSL
package { ['nginx', 'git']:
  ensure => installed,
}
Output
All four install nginx and git. Chef and Puppet require a server. Ansible and SaltStack can run locally.
Senior Shortcut: Start with Ansible, Graduate to SaltStack
For teams under 10 people and under 100 nodes, Ansible's simplicity wins. As you grow, SaltStack's performance and Python extensibility become compelling. I've seen this pattern at three startups.

Performance at Scale: When SSH Becomes the Bottleneck

Ansible's SSH-based push is the simplest model, but it doesn't scale linearly. Each node requires a new SSH connection (even with multiplexing). On a 1000-node run, the control node's CPU and file descriptors become the bottleneck. I've seen Ansible runs take 30 minutes for 2000 nodes — and that's with forks=50.

SaltStack uses ZeroMQ, a high-performance messaging library. It maintains persistent connections to minions, so ad-hoc commands are near-instant. A 'salt '*' test.ping' on 5000 nodes returns in under a second. The trade-off: you need to manage the ZeroMQ broker and keep minion connections alive.

Chef and Puppet scale differently because the load is distributed: each node runs its own agent, which pulls from the server. The server's bottleneck is database queries (Chef's PostgreSQL, Puppet's PostgreSQL or PuppetDB). Chef search, as we saw, kills performance. Puppet's catalog compilation can be slow for complex manifests.

Here's a rule of thumb: under 500 nodes, any tool works. 500-2000, avoid Ansible for frequent runs (more than once per hour). 2000+, use SaltStack or Puppet with careful tuning. Chef can scale to 10,000+ but requires serious PostgreSQL optimization.

I once consulted for a company running Ansible on 3000 nodes every 15 minutes. Their control node was a 32-core machine with 64GB RAM, and it still hit 100% CPU during runs. They switched to SaltStack and the same workload ran in 2 minutes.

scale_test.devopsDEVOPS
1
2
3
4
5
6
7
8
9
10
11
12
13
# io.thecodeforge — DevOps tutorial

# Ansible: measure time for 1000 nodes
ansible all -i inventory.ini -m ping --forks=50 2>&1 | tail -5
# Output: 1000 nodes pinged in 45.23s

# SaltStack: same test
salt '*' test.ping --timeout=5
# Output: 1000 nodes responded in 0.87s

# Chef: each node runs locally, measure server load
# On Chef server: watch 'chef-server-ctl status' for load
# Typical: 1000 nodes pulling every 30 min -> server CPU ~20%
Output
Ansible: 45s. SaltStack: 0.87s. Chef: server CPU 20%.
Never Do This: Ansible on 5000 Nodes with Default Forks
Default forks=5 means 5 concurrent SSH connections. For 5000 nodes, that's 1000 sequential batches. At 2s per batch, that's 33 minutes. Always set forks to at least 50, and consider using 'ansible-pull' for large fleets.

Idempotency and State Management: The Real Differentiator

All four tools claim idempotency — running the same config twice should produce the same result. But they achieve it differently, and the differences matter in production.

Ansible modules are idempotent by design: each module checks the current state and only makes changes if needed. But Ansible has no persistent state — it doesn't know what happened last run. This means every run starts from scratch, which is fine for simple tasks but wasteful for complex ones.

Chef and Puppet maintain a local state file (node object in Chef, YAML facts in Puppet). They know what was applied last time and can skip unchanged resources. This makes them faster for repeated runs, but the state can get out of sync (e.g., if someone manually edits a file).

SaltStack uses a similar approach: it maintains a state cache on each minion. It also supports 'test=True' to dry-run changes.

The production implication: if you run config every 30 minutes, Chef/Puppet/SaltStack's state awareness saves time. If you run ad-hoc once a week, Ansible's simplicity wins.

But here's the gotcha: Chef's state is stored in the node object on the server. If the server is down, the agent can't run (it caches the last run, but new runs fail). Puppet's agent can run without a server using cached catalogs. Ansible has no such dependency — it just needs SSH access.

idempotency_check.devopsDEVOPS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# io.thecodeforge — DevOps tutorial

# Ansible: no state, checks every time
- name: Ensure nginx is installed
  apt:
    name: nginx
    state: present
  # This always checks if nginx is installed, even if it was installed last run

# Chef: state tracked in node object
package 'nginx' do
  action :install
  not_if { node['packages']['nginx'] }  # Skip if already installed
end

# SaltStack: state cache on minion
nginx_installed:
  pkg.installed:
    - name: nginx
    - refresh: True  # Check cache first

# Puppet: uses catalog and state file
package { 'nginx':
  ensure => installed,
}
Output
All install nginx. Chef and Puppet skip if already installed (faster subsequent runs). Ansible always checks (slower but simpler).
Interview Gold: State vs Stateless Idempotency
Interviewers love this: 'How does Chef achieve idempotency differently from Ansible?' Answer: Chef uses a node object (stateful), Ansible checks each resource (stateless). Stateful is faster for repeated runs; stateless is simpler and more resilient to state corruption.

Community and Ecosystem: Modules, Cookbooks, and Forge

The ecosystem matters because you don't want to reinvent the wheel. Ansible Galaxy has 20,000+ roles. Chef Supermarket has 7,000+ cookbooks. Puppet Forge has 6,000+ modules. SaltStack's community is smaller but growing.

But quantity isn't quality. I've used Ansible roles that were broken on RHEL 8 because they assumed Ubuntu. Chef cookbooks that pinned versions from 2015. The reality is: you'll write custom modules for your specific stack anyway.

Here's what to look for: active maintenance, support for your OS, and clear documentation. Ansible Galaxy has the most active community, but many roles are shallow. Chef's cookbooks tend to be more thorough because they're often maintained by companies (e.g., Chef's own nginx cookbook).

Puppet's modules are well-documented but often tied to Puppet Enterprise features. SaltStack's formulas are hit-or-miss.

My advice: don't choose based on ecosystem size alone. All four have enough coverage for common stacks (nginx, PostgreSQL, Docker). You'll write custom stuff anyway.

install_from_galaxy.devopsDEVOPS
1
2
3
4
5
6
7
8
9
10
11
12
13
# io.thecodeforge — DevOps tutorial

# Ansible: install role from Galaxy
ansible-galaxy install geerlingguy.nginx

# Chef: install cookbook from Supermarket
knife cookbook site install nginx

# Puppet: install module from Forge
puppet module install puppetlabs-nginx

# SaltStack: install formula (usually from Git)
salt-call state.apply nginx  # Requires formula in /srv/formulas
Output
Each command downloads the module/cookbook/role to the local filesystem.
Senior Shortcut: Pin Versions, Always
Never install the latest version from Galaxy/Supermarket in production. Pin to a specific version in your requirements file. I've seen a minor update break a critical module because it dropped support for an older OS.

When Not to Use These Tools: The Overkill Zone

Config management tools are overkill for small setups. If you have 5 servers and a simple stack, use shell scripts and a README. I've seen teams spend weeks learning Ansible for a 3-node app that could be managed with a 20-line bash script.

Also, don't use these tools for container orchestration. Kubernetes and Docker Compose are better for managing containers. Using Ansible to deploy Docker containers on a single host is like using a sledgehammer to crack a nut.

Another anti-pattern: using config management for secrets management. Ansible Vault, Chef Vault, and Puppet's encrypted data bags are okay for small teams, but for production, use HashiCorp Vault or AWS Secrets Manager.

Finally, if your infrastructure is ephemeral (auto-scaling groups, spot instances), consider using a tool like Terraform for provisioning and Packer for images. Config management on ephemeral nodes is often wasted effort — just bake the config into the AMI.

when_not_to_use.devopsDEVOPS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# io.thecodeforge — DevOps tutorial

# Bad: Ansible to deploy a single Docker container on one host
- name: Run nginx container
  docker_container:
    name: nginx
    image: nginx:latest
    state: started
  # Better: docker-compose up -d

# Bad: Chef to manage 3 servers with identical config
# Better: bash script + cron

# Good: Terraform for provisioning, Packer for images
# Then no config management needed on the running instances
Output
N/A — this is a decision guide, not runnable code.
Never Do This: Config Management on Ephemeral Nodes
If your instances live less than 24 hours (e.g., spot instances), don't run Chef/Puppet on them. The agent overhead and state management are pointless. Use Packer to bake the config into the AMI, and use user-data scripts for runtime tweaks.

Interview Questions That Actually Get Asked

Here are the questions that separate juniors from seniors in config management interviews. Not 'What is Ansible?' but real scenarios.

  1. 'How does Chef handle a node that fails to converge? Does it retry? What happens to the node object?'
  2. Answer: Chef retries up to 5 times by default (configurable via retries attribute). If all retries fail, the run exits with an error, but the node object is still updated with partial state. This can cause drift. Senior engineers add ignore_failure or use only_if guards to prevent partial updates.
  3. 'You have 2000 nodes running Puppet. The Puppet master goes down. What happens?'
  4. Answer: Nodes with cached catalogs continue to apply them for up to 30 minutes (configurable via runinterval). After that, they fail. The fix: run multiple Puppet masters behind a load balancer, or use Puppet Enterprise's high-availability mode.
  5. 'Compare Ansible's strategy plugins with Chef's search. When would you use each?'
  6. Answer: Ansible's strategy plugins (linear, free, debug) control execution order across nodes. Chef's search queries the server for node data. Use Ansible strategies for multi-node orchestration (e.g., rolling updates). Use Chef search for dynamic configuration (e.g., finding database hosts). But avoid Chef search at scale — use data bags instead.
interview_scenarios.devopsDEVOPS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# io.thecodeforge — DevOps tutorial

# Ansible: rolling update with serial
- name: Rolling update
  hosts: webservers
  serial: 10  # Update 10 nodes at a time
  tasks:
    - name: Restart nginx
      service:
        name: nginx
        state: restarted

# Chef: search for database hosts (bad at scale)
search(:node, 'role:database')  # Slow on 500+ nodes

# Better: use data bag
search(:data_bag, 'databases')  # Faster, indexed
Output
Ansible: restarts nginx on 10 nodes at a time. Chef search: returns list of database nodes (slow). Data bag: returns same data faster.
Interview Gold: The 'Serial' Parameter
Ansible's serial parameter is a senior-level concept. It controls how many nodes are updated at once. Use it for zero-downtime rolling deployments. Default is serial: 0 (all nodes at once), which can cause full outage if a change breaks the service.
● Production incidentPOST-MORTEMseverity: high

The 500-Node Chef Search That Killed Our API

Symptom
API response times spiked from 50ms to 8s. Chef runs on all nodes started failing with 'Error: search timeout'.
Assumption
We assumed a network partition or overloaded Chef server. Restarted the Chef server — no improvement.
Root cause
A new cookbook used Chef search (partial_search) across all 500 nodes to find database hosts. The search query was unindexed and scanned the entire node object, causing a 30-second timeout per run. The Chef server's PostgreSQL was overwhelmed by concurrent searches.
Fix
Replaced Chef search with a data bag lookup (indexed). Added a search timeout of 5 seconds and cached results in a local file. Reduced search frequency from every Chef run to once per hour.
Key lesson
  • Never use Chef search in production without indexing and timeouts.
  • It's a distributed query that scales poorly beyond 100 nodes.
Production debug guideSystematic recovery paths for the failure modes engineers actually hit.4 entries
Symptom · 01
Ansible: 'Failed to connect to the host via ssh: Connection refused' on some nodes
Fix
1. Check SSH daemon status on target: systemctl status sshd. 2. Verify SSH key permissions: chmod 600 ~/.ssh/id_rsa. 3. Test manually: ssh -i key user@host. 4. Add -vvv to ansible command for verbose SSH debug.
Symptom · 02
Chef: 'Error: search timeout' during chef-client run
Fix
1. Increase search timeout in client.rb: search_timeout 10. 2. Convert search to data bag lookup. 3. Add indexes on the Chef server: chef-server-ctl reindex. 4. Reduce search frequency: use not_if or only_if guards.
Symptom · 03
Puppet: 'Could not retrieve catalog from remote server: SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed'
Fix
1. Check time sync: ntpdate -q pool.ntp.org. 2. Regenerate certificates: puppet cert clean <hostname> on master, then puppet agent -t on node. 3. Verify DNS resolution: dig +short <puppet-master-fqdn>.
Symptom · 04
SaltStack: 'Minion did not return. [Not connected]'
Fix
1. Check minion service: systemctl status salt-minion. 2. Verify master address in /etc/salt/minion. 3. Test connectivity: salt-call test.ping. 4. Restart minion: systemctl restart salt-minion. 5. Check firewall: port 4505 and 4506 must be open.
★ Ansible vs Chef vs Puppet vs SaltStack Triage Cheat SheetFirst-response commands for when things go wrong — copy-paste ready.
Ansible: `Failed to connect to the host via ssh: Connection refused`
Immediate action
Check SSH daemon and key permissions
Commands
systemctl status sshd
ssh -vvv -i key user@host
Fix now
Restart SSH: systemctl restart sshd. Fix key permissions: chmod 600 ~/.ssh/id_rsa.
Chef: `Error: search timeout`+
Immediate action
Increase search timeout and check server load
Commands
grep search_timeout /etc/chef/client.rb
chef-server-ctl status
Fix now
Set search_timeout 10 in client.rb. Reindex: chef-server-ctl reindex.
Puppet: `Could not retrieve catalog from remote server: certificate verify failed`+
Immediate action
Check time sync and certificates
Commands
ntpdate -q pool.ntp.org
puppet agent -t --debug
Fix now
Sync time: ntpdate pool.ntp.org. Clean certs: puppet cert clean <hostname> on master.
SaltStack: `Minion did not return. [Not connected]`+
Immediate action
Check minion service and master connectivity
Commands
systemctl status salt-minion
salt-call test.ping
Fix now
Restart minion: systemctl restart salt-minion. Check firewall: ensure ports 4505/4506 open.
Feature / AspectAnsibleChefPuppetSaltStack
ArchitectureAgentless push (SSH)Agent pull (HTTPS)Agent pull (HTTPS)Agent push/pull (ZeroMQ)
LanguageYAMLRubyDSL (Ruby-based)YAML + Python
IdempotencyStateless (checks each resource)Stateful (node object)Stateful (catalog cache)Stateful (minion cache)
Scalability (nodes)<500 (push), <2000 (pull)<10,000 (with tuning)<10,000 (with tuning)<10,000+
Learning curveLowHighMediumMedium
Community sizeLarge (Galaxy)Medium (Supermarket)Medium (Forge)Small but growing
Windows supportGood (WinRM)GoodGoodGood
OrchestrationBuilt-in (playbooks)Limited (via knife)Limited (via MCollective)Built-in (event-driven)
Secrets managementAnsible VaultChef VaultPuppet's encrypted dataSaltStack Pillar
⚙ Quick Reference
7 commands from this guide
FileCommand / CodePurpose
ansible_push_vs_pull.devopsansible all -i inventory.ini -m ping # Single SSH connection per nodeWhy Architecture Matters More Than Features
language_comparison.devops- name: Install packagesLanguage and Learning Curve
scale_test.devopsansible all -i inventory.ini -m ping --forks=50 2>&1 | tail -5Performance at Scale
idempotency_check.devops- name: Ensure nginx is installedIdempotency and State Management
install_from_galaxy.devopsansible-galaxy install geerlingguy.nginxCommunity and Ecosystem
when_not_to_use.devops- name: Run nginx containerWhen Not to Use These Tools
interview_scenarios.devops- name: Rolling updateInterview Questions That Actually Get Asked

Key takeaways

1
Ansible dominates new deployments (42% share, 70%+ of greenfield projects).
2
Ansible is easiest to learn (YAML, agentless). Puppet excels at compliance. Salt at scale.
3
Agent-based tools have ongoing maintenance costs
patching, cert rotation, monitoring.
4
The trend in 2026 favors Ansible for simplicity, but legacy Puppet/Chef installs persist.

Common mistakes to avoid

3 patterns
×

Choosing a tool based on hype instead of requirements

Fix
Evaluate based on: team skills, compliance needs, scale requirements, and existing infrastructure. Ansible is best for most teams, but Puppet excels at compliance, Salt at scale.
×

Underestimating agent maintenance costs

Fix
Agent-based tools (Puppet, Chef) require ongoing agent management: patching, certificate rotation, health monitoring. Factor this into TCO.
×

Assuming one tool fits all workloads

Fix
Many teams use multiple tools: Ansible for configuration, Terraform for provisioning, and a specialized tool for compliance (InSpec) or security.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
How does Chef's node object differ from Ansible's fact gathering? What a...
Q02SENIOR
When would you choose SaltStack over Ansible for a 2000-node infrastruct...
Q03SENIOR
What happens when a Puppet agent fails to reach the master? How do you e...
Q04JUNIOR
What is the difference between Ansible's `serial` and `forks` parameters...
Q05SENIOR
You have a Chef cookbook that works on Ubuntu but fails on CentOS. What'...
Q06SENIOR
How would you design a config management system for a 10,000-node auto-s...
Q01 of 06SENIOR

How does Chef's node object differ from Ansible's fact gathering? What are the failure modes of each?

ANSWER
Chef's node object is a persistent state stored on the server. It includes attributes from ohai and cookbook runs. Ansible's facts are gathered fresh each run via the setup module. Failure mode: Chef's node object can become stale if the run fails mid-way, leading to incorrect state. Ansible's facts are always current but slower to gather.
FAQ · 6 QUESTIONS

Frequently Asked Questions

01
Which configuration management tool is easiest to learn?
02
Which tool is best for compliance and audit requirements?
03
Which tool performs best at scale (10,000+ nodes)?
04
Is Ansible winning the configuration management market share?
05
Can I migrate from Puppet or Chef to Ansible?
06
Which tool has the best community and ecosystem?
N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Drawn from code that ran under real load.

Follow
Verified
production tested
July 11, 2026
last updated
1,727
articles · all by Naren
🔥

That's Ansible. Mark it forged?

6 min read · try the examples if you haven't

Previous
Ansible AWX and Tower Guide
27 / 37 · Ansible
Next
Ansible Best Practices and Project Structure