DevToolBoxFREE
Blog

Ansible Complete Guide: Infrastructure Automation Made Simple

22 min readby DevToolBox Team

TL;DR

Ansible is an agentless automation tool that uses SSH and YAML playbooks to configure servers, deploy applications, and orchestrate infrastructure. Define your hosts in an inventory, write playbooks with tasks and handlers, organize reusable logic into roles, encrypt secrets with Vault, and use Jinja2 templates for dynamic configuration files. It works seamlessly with Docker, Kubernetes, and cloud providers. Format your YAML configs with the online YAML formatter.

1. What Is Ansible?

Ansible is an open-source IT automation engine developed by Red Hat. It automates configuration management, application deployment, cloud provisioning, and orchestration. Unlike Chef or Puppet, Ansible is agentless — it connects to managed nodes over SSH (or WinRM for Windows) and requires no software to be installed on target machines.

Ansible uses a push-based model: you run commands from a control node, and Ansible pushes changes to remote hosts. Playbooks are written in YAML, making them human-readable and easy to version control. The idempotent design means running a playbook multiple times produces the same result — it only changes what needs changing.

2. Installing Ansible

Ansible only needs to be installed on the control node (your workstation or a CI server). Managed nodes only need Python and SSH.

# Ubuntu / Debian
sudo apt update
sudo apt install -y ansible

# RHEL / CentOS / Fedora
sudo dnf install -y ansible-core

# macOS (Homebrew)
brew install ansible

# pip (any OS — recommended for latest version)
pip install ansible

# Verify installation
ansible --version
# ansible [core 2.16.x]

3. Inventory Files — Defining Your Infrastructure

An inventory tells Ansible which hosts to manage. It can be a simple INI file, YAML file, or a dynamic script that queries cloud APIs. The default location is /etc/ansible/hosts.

INI Format

# inventory.ini
[webservers]
web1.example.com ansible_user=ubuntu
web2.example.com ansible_user=ubuntu

[dbservers]
db1.example.com ansible_user=root ansible_port=2222

[production:children]
webservers
dbservers

[production:vars]
ansible_ssh_private_key_file=~/.ssh/prod_key

YAML Format

# inventory.yml
all:
  children:
    webservers:
      hosts:
        web1.example.com:
          ansible_user: ubuntu
        web2.example.com:
          ansible_user: ubuntu
    dbservers:
      hosts:
        db1.example.com:
          ansible_user: root
          ansible_port: 2222

4. Ad-Hoc Commands — Quick One-Liners

Ad-hoc commands let you run a single task on remote hosts without writing a playbook. They are perfect for quick checks, one-off operations, and troubleshooting.

# Ping all hosts
ansible all -i inventory.ini -m ping

# Check disk space on webservers
ansible webservers -m shell -a "df -h"

# Install a package
ansible dbservers -m apt -a "name=postgresql state=present" --become

# Copy a file
ansible all -m copy -a "src=/tmp/config.conf dest=/etc/app/config.conf"

# Restart a service
ansible webservers -m service -a "name=nginx state=restarted" --become

# Gather facts
ansible web1.example.com -m setup

5. Playbooks — Tasks, Handlers, and Variables

A playbook is a YAML file containing one or more plays. Each play targets a group of hosts and defines a list of tasks to execute in order. Playbooks are the core of Ansible automation.

Basic Playbook Structure

# site.yml
---
- name: Configure web servers
  hosts: webservers
  become: yes
  vars:
    http_port: 80
    app_name: myapp

  tasks:
    - name: Install Nginx
      apt:
        name: nginx
        state: present
        update_cache: yes

    - name: Copy Nginx config
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/sites-available/default
      notify: Restart Nginx

    - name: Ensure Nginx is running
      service:
        name: nginx
        state: started
        enabled: yes

  handlers:
    - name: Restart Nginx
      service:
        name: nginx
        state: restarted

Handlers are special tasks that run only when notified by another task. They execute once at the end of the play, even if notified multiple times. This is ideal for restarting services only when configuration files actually change.

Variables in Playbooks

# Variable precedence (lowest to highest):
# 1. role defaults
# 2. inventory vars
# 3. playbook vars
# 4. role vars
# 5. task vars
# 6. extra vars (-e)

- name: Deploy with variables
  hosts: webservers
  vars:
    app_version: "2.1.0"
    deploy_dir: /opt/myapp
  vars_files:
    - vars/common.yml
    - vars/production.yml

  tasks:
    - name: Create deploy directory
      file:
        path: "{{ deploy_dir }}"
        state: directory
        mode: "0755"

# Run with extra vars
# ansible-playbook site.yml -e "app_version=2.2.0"

6. Essential Modules — apt, yum, copy, template, service, file, user

Ansible ships with thousands of modules. Here are the most commonly used ones for system administration.

Package Management (apt / yum)

# Install packages (Debian/Ubuntu)
- name: Install required packages
  apt:
    name:
      - nginx
      - python3-pip
      - git
      - curl
    state: present
    update_cache: yes
    cache_valid_time: 3600

# Install packages (RHEL/CentOS)
- name: Install packages
  yum:
    name:
      - httpd
      - php
      - mariadb-server
    state: latest

File Operations (copy / template / file)

# Copy a static file
- name: Copy app config
  copy:
    src: files/app.conf
    dest: /etc/myapp/app.conf
    owner: root
    group: root
    mode: "0644"
    backup: yes

# Deploy a Jinja2 template
- name: Deploy Nginx config
  template:
    src: templates/nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    validate: "nginx -t -c %s"
  notify: Reload Nginx

# Create directories and set permissions
- name: Create app directories
  file:
    path: /opt/myapp/logs
    state: directory
    owner: appuser
    group: appuser
    mode: "0755"
    recurse: yes

Service and User Management

# Manage services
- name: Start and enable Nginx
  service:
    name: nginx
    state: started
    enabled: yes

# Manage system users
- name: Create application user
  user:
    name: appuser
    comment: "Application Service Account"
    shell: /bin/bash
    groups: www-data
    append: yes
    create_home: yes

# Add SSH key for user
- name: Add authorized key
  authorized_key:
    user: appuser
    key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"

7. Roles and Ansible Galaxy

Roles are the primary mechanism for organizing and reusing Ansible content. A role groups tasks, handlers, templates, files, and variables into a standardized directory structure.

# Role directory structure
roles/
  nginx/
    tasks/
      main.yml        # Entry point for tasks
    handlers/
      main.yml        # Handlers
    templates/
      nginx.conf.j2   # Jinja2 templates
    files/
      index.html      # Static files
    vars/
      main.yml        # Role variables (high precedence)
    defaults/
      main.yml        # Default variables (easily overridden)
    meta/
      main.yml        # Dependencies, metadata

Using Roles in Playbooks

# site.yml — using roles
---
- name: Configure web stack
  hosts: webservers
  become: yes
  roles:
    - common
    - nginx
    - role: app_deploy
      vars:
        app_version: "3.0.0"
    - role: monitoring
      when: enable_monitoring | default(true)

Ansible Galaxy

# Initialize a new role skeleton
ansible-galaxy role init my_role

# Install a role from Galaxy
ansible-galaxy role install geerlingguy.docker

# Install roles from requirements file
# requirements.yml
---
roles:
  - name: geerlingguy.docker
    version: "7.1.0"
  - name: geerlingguy.nginx
  - name: geerlingguy.postgresql

collections:
  - name: community.docker
    version: "3.4.0"

# Install all from requirements
ansible-galaxy install -r requirements.yml

8. Variables and Facts

Facts are system information automatically gathered from managed nodes. Ansible collects facts about the OS, network interfaces, disks, memory, and more using the setup module.

# Access facts in playbooks
- name: Show system information
  debug:
    msg: |
      OS: {{ ansible_distribution }} {{ ansible_distribution_version }}
      IP: {{ ansible_default_ipv4.address }}
      CPUs: {{ ansible_processor_vcpus }}
      RAM: {{ ansible_memtotal_mb }} MB
      Hostname: {{ ansible_hostname }}

# Register task output as a variable
- name: Check if app is running
  shell: systemctl is-active myapp
  register: app_status
  ignore_errors: yes

- name: Start app if not running
  service:
    name: myapp
    state: started
  when: app_status.rc != 0

# Custom facts (place in /etc/ansible/facts.d/*.fact)
# /etc/ansible/facts.d/app.fact
# [general]
# version=2.1.0
# env=production
# Access: {{ ansible_local.app.general.version }}

9. Conditionals and Loops

Ansible supports when conditionals and various loop constructs to make playbooks dynamic and adaptable to different environments.

# Conditionals
- name: Install Apache on RedHat
  yum:
    name: httpd
    state: present
  when: ansible_os_family == "RedHat"

- name: Install Apache on Debian
  apt:
    name: apache2
    state: present
  when: ansible_os_family == "Debian"

# Loops with loop (replaces with_items)
- name: Create multiple users
  user:
    name: "{{ item.name }}"
    groups: "{{ item.groups }}"
    state: present
  loop:
    - { name: alice, groups: admin }
    - { name: bob, groups: developers }
    - { name: charlie, groups: developers }

# Loop with dict2items
- name: Set sysctl parameters
  sysctl:
    name: "{{ item.key }}"
    value: "{{ item.value }}"
    sysctl_set: yes
  loop: "{{ sysctl_params | dict2items }}"
  vars:
    sysctl_params:
      net.core.somaxconn: 65535
      vm.swappiness: 10

10. Jinja2 Templates

The template module renders Jinja2 templates with variables and logic, then deploys the result to remote hosts. Templates are essential for generating configuration files dynamically.

# templates/nginx.conf.j2
upstream app_backend {
{% for server in app_servers %}
    server {{ server }}:{{ app_port | default(8080) }};
{% endfor %}
}

server {
    listen {{ http_port | default(80) }};
    server_name {{ domain_name }};

{% if ssl_enabled | default(false) %}
    listen 443 ssl;
    ssl_certificate     /etc/ssl/{{ domain_name }}.crt;
    ssl_certificate_key /etc/ssl/{{ domain_name }}.key;
{% endif %}

    location / {
        proxy_pass http://app_backend;
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
    }

    # Generated by Ansible on {{ ansible_date_time.iso8601 }}
    # Managed by: {{ ansible_user_id }}
}

Jinja2 provides powerful filters: {{ list | join(",") }}, {{ var | default("fallback") }},{{ password | hash("sha512") }}, and {{ path | basename }}.

11. Ansible Vault — Managing Secrets

Ansible Vault encrypts sensitive data (passwords, API keys, certificates) so they can be safely stored in version control. Files are encrypted with AES-256.

# Create / encrypt / edit / view encrypted files
ansible-vault create secrets.yml
ansible-vault encrypt vars/production.yml
ansible-vault edit secrets.yml
ansible-vault view secrets.yml

# Encrypt a single string (inline vault)
ansible-vault encrypt_string 'SuperSecret123' --name 'db_password'

# Run playbook with vault password
ansible-playbook site.yml --ask-vault-pass
ansible-playbook site.yml --vault-password-file ~/.vault_pass

# Multiple vault IDs for different environments
ansible-playbook site.yml \
  --vault-id dev@~/.vault_dev \
  --vault-id prod@~/.vault_prod

12. Error Handling

Ansible provides block/rescue/always for structured error handling, similar to try/catch/finally in programming languages.

- name: Deploy with error handling
  hosts: webservers
  tasks:
    - name: Deployment block
      block:
        - name: Pull latest code
          git:
            repo: https://github.com/org/app.git
            dest: /opt/app
            version: main

        - name: Run database migrations
          command: /opt/app/manage.py migrate

        - name: Restart application
          service:
            name: myapp
            state: restarted

      rescue:
        - name: Rollback on failure
          command: /opt/app/rollback.sh

        - name: Send failure notification
          mail:
            to: ops@example.com
            subject: "Deployment FAILED on {{ inventory_hostname }}"
            body: "Deployment failed. Rollback executed."

      always:
        - name: Cleanup temp files
          file:
            path: /tmp/deploy_artifacts
            state: absent

# Retry with custom failure condition
- name: Check URL (with retries)
  uri:
    url: http://localhost:8080/health
  register: health_check
  retries: 5
  delay: 10
  until: health_check.status == 200

13. Tags — Selective Task Execution

Tags let you run specific subsets of tasks in a playbook. This is invaluable for large playbooks where you only need to update one part of the configuration.

- name: Full server setup
  hosts: all
  tasks:
    - name: Install base packages
      apt:
        name: [vim, htop, curl, git]
        state: present
      tags: [packages, base]

    - name: Configure firewall
      ufw:
        rule: allow
        port: "{{ item }}"
      loop: ["22", "80", "443"]
      tags: [firewall, security]

    - name: Deploy application
      copy:
        src: app/
        dest: /opt/myapp/
      tags: [deploy, app]

# Run only specific tags
# ansible-playbook site.yml --tags "deploy"
# ansible-playbook site.yml --tags "security,packages"

# Skip specific tags
# ansible-playbook site.yml --skip-tags "firewall"

# List all tags in a playbook
# ansible-playbook site.yml --list-tags

14. Dynamic Inventory

Static inventory files work for small environments, but cloud infrastructure changes constantly. Dynamic inventory scripts or plugins query cloud APIs (AWS, GCP, Azure) to discover hosts automatically.

# AWS EC2 dynamic inventory plugin
# aws_ec2.yml
---
plugin: amazon.aws.aws_ec2
regions:
  - us-east-1
  - us-west-2
filters:
  tag:Environment:
    - production
  instance-state-name:
    - running
keyed_groups:
  - key: tags.Role
    prefix: role
  - key: placement.region
    prefix: aws_region
compose:
  ansible_host: public_ip_address

# Use dynamic inventory
# ansible-inventory -i aws_ec2.yml --list
# ansible-playbook -i aws_ec2.yml site.yml

For GCP, use google.cloud.gcp_compute; for Azure, use azure.azcollection.azure_rm. All follow the same pattern: filters, authentication, and host grouping.

15. Ansible with Docker and Kubernetes

Ansible integrates with container platforms through dedicated collections: community.docker for Docker and kubernetes.core for Kubernetes.

Docker Management

# Install collection first:
# ansible-galaxy collection install community.docker

- name: Manage Docker containers
  hosts: docker_hosts
  tasks:
    - name: Pull application image
      community.docker.docker_image:
        name: myapp
        tag: "2.0"
        source: pull

    - name: Run application container
      community.docker.docker_container:
        name: myapp
        image: "myapp:2.0"
        state: started
        restart_policy: unless-stopped
        ports:
          - "8080:8080"
        env:
          DB_HOST: "db.internal"
          NODE_ENV: "production"
        volumes:
          - /data/app:/app/data

Kubernetes Deployments

# ansible-galaxy collection install kubernetes.core

- name: Deploy to Kubernetes
  hosts: localhost
  tasks:
    - name: Create namespace
      kubernetes.core.k8s:
        state: present
        definition:
          apiVersion: v1
          kind: Namespace
          metadata:
            name: myapp-prod

    - name: Deploy application from manifest
      kubernetes.core.k8s:
        state: present
        src: k8s/deployment.yml
        namespace: myapp-prod

16. AWX and Ansible Automation Platform (Tower)

AWX is the open-source upstream project for the Red Hat Ansible Automation Platform (formerly Ansible Tower). It adds a web UI, REST API, role-based access control, job scheduling, and centralized credential management on top of command-line Ansible.

# Deploy AWX on Kubernetes via AWX Operator
kubectl apply -f https://raw.githubusercontent.com/ansible/\
  awx-operator/main/deploy/awx-operator.yml

# awx-instance.yml
---
apiVersion: awx.ansible.com/v1beta1
kind: AWX
metadata:
  name: awx
  namespace: awx
spec:
  service_type: NodePort

kubectl apply -f awx-instance.yml
kubectl get pods -n awx -w

Key AWX features include Job Templates, Workflows (chaining templates), RBAC, encrypted credential storage, cron-like scheduling, and a REST API for CI/CD integration.

17. Ansible Best Practices

Following these conventions keeps your Ansible projects maintainable as they grow.

# Recommended project layout
ansible-project/
  inventories/
    production/
      hosts.yml
      group_vars/all.yml
      host_vars/web1.example.com.yml
    staging/
      hosts.yml
  roles/
    common/
    nginx/
    app/
  playbooks/
    site.yml
    webservers.yml
  ansible.cfg
  requirements.yml

Key best practices:

  • Always name your tasks clearly — name: Install Nginx beats an unnamed task.
  • Use roles for reusability; keep playbooks as thin orchestration layers.
  • Pin role and collection versions in requirements.yml.
  • Store secrets with Ansible Vault, never in plain text.
  • Use ansible-lint to enforce coding standards.
  • Test with Molecule before deploying to production.
  • Use --check (dry run) and --diff to preview changes.
  • Keep inventory per environment in separate directories.
  • Use group_vars and host_vars instead of inline variables.
  • Avoid using shell/command when a dedicated module exists.
# ansible.cfg — project-level configuration
[defaults]
inventory = inventories/production/hosts.yml
roles_path = roles
retry_files_enabled = False
stdout_callback = yaml
forks = 20
timeout = 30

[privilege_escalation]
become = True
become_method = sudo
become_ask_pass = False

[ssh_connection]
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=60s

18. Ansible vs Terraform vs Chef vs Puppet

FeatureAnsibleTerraformChefPuppet
Primary UseConfig managementInfrastructure provisioningConfig managementConfig management
LanguageYAMLHCLRuby DSLPuppet DSL
AgentAgentless (SSH)Agentless (API)Agent requiredAgent required
ModelPushPushPullPull
StateStatelessState fileChef ServerPuppetDB
Learning CurveLowMediumHighMedium-High
Best ForServer config, app deployCloud infra provisioningComplex enterprise configLarge-scale compliance

Many teams combine Terraform + Ansible: Terraform provisions infrastructure (VMs, networks, load balancers), then Ansible configures those machines (install packages, deploy apps, manage services). Use the YAML formatter to keep both Terraform YAML and Ansible playbooks clean.

Key Takeaways

  • Agentless by design: Ansible uses SSH and requires no agent installation on managed nodes, simplifying setup dramatically.
  • YAML playbooks: human-readable, version-controllable, and idempotent; running them twice produces the same result.
  • Roles for reusability: break complex automation into roles and share them via Ansible Galaxy.
  • Vault for secrets: encrypt passwords and API keys with AES-256; use vault IDs for multi-environment setups.
  • Jinja2 templates: generate dynamic configuration files with variables, loops, and conditionals.
  • Dynamic inventory: query AWS, GCP, or Azure APIs to discover hosts automatically instead of hardcoding IPs.
  • Docker and Kubernetes modules: manage containers and cluster resources directly from playbooks.
  • Ansible + Terraform: use Terraform to provision infrastructure, then Ansible to configure it; they complement each other.

Frequently Asked Questions

What is Ansible and how does it work?

Ansible is an agentless automation tool that connects over SSH and executes tasks from YAML playbooks. It uses a push model and requires no software on managed nodes.

What is the difference between Ansible and Terraform?

Terraform provisions infrastructure (VMs, networks) with a state file. Ansible configures existing machines (packages, services). Many teams use both together.

What is the difference between Ansible and Chef/Puppet?

Ansible is agentless (SSH) with YAML playbooks. Chef uses Ruby DSL and Puppet its own language, both requiring agents. Ansible pushes; Chef and Puppet pull.

How do I manage secrets in Ansible securely?

Use Ansible Vault for AES-256 encryption. Create encrypted files with ansible-vault create, encrypt strings inline, and run with --ask-vault-pass or a password file.

What are Ansible roles and why should I use them?

Roles package tasks, handlers, templates, and variables into a reusable directory structure. Share them via Ansible Galaxy and compose across projects.

How does Ansible work with Docker and Kubernetes?

Use community.docker for containers and images, kubernetes.core for pods and deployments. Ansible orchestrates the full lifecycle from build to rollout.

What is Ansible AWX/Tower and when do I need it?

AWX adds a web UI, REST API, RBAC, and job scheduling on top of CLI Ansible. Use it when teams need shared access, audit trails, or scheduled automation runs.

How do I handle errors and failures in Ansible playbooks?

Use block/rescue/always for try/catch/finally handling. Combine with ignore_errors, failed_when, and retries/until for robust automation.

𝕏 Twitterin LinkedIn
Was this helpful?

Stay Updated

Get weekly dev tips and new tool announcements.

No spam. Unsubscribe anytime.

Try These Related Tools

{ }JSON Formatter📋YAML Formatter

Related Articles

Prometheus Complete Guide: Monitoring and Alerting for Modern Infrastructure

Master Prometheus with metric types, PromQL, recording rules, alerting, Alertmanager, exporters, Grafana integration, Kubernetes monitoring, and long-term storage.

Terraform Complete Guide: Infrastructure as Code from Basics to CI/CD

Master Terraform infrastructure as code. Complete guide with HCL syntax, modules, state management, AWS provider, workspaces, Terraform Cloud, testing with tfsec/checkov/Terratest, and best practices.

Docker Commands: Complete Guide from Basics to Production

Master Docker with this complete commands guide. Covers docker run/build/push, Dockerfile, multi-stage builds, volumes, networking, Docker Compose, security, registry, and production deployment patterns.