Ansible has transformed IT automation by making complex infrastructure management accessible through simple, human-readable YAML files. In 2026, Ansible remains the preferred automation tool for system administrators, DevOps engineers, and cloud architects worldwide. This comprehensive guide takes you from Ansible basics to advanced automation techniques. ## What is Ansible? Ansible is an open-source automation platform that enables infrastructure as code, configuration management, application deployment, and orchestration. Unlike traditional configuration management tools, Ansible is agentless—it connects to remote systems via SSH or WinRM, requiring no additional software on managed nodes. ### Key Features - Agentless Architecture: No agents to install or manage
Simple Syntax: YAML-based playbooks anyone can read
Idempotent: Safe to run multiple times
Extensible: Thousands of modules for various tasks
Push-Based: Changes pushed from control node ## Installing Ansible ### macOS ```bash
Using Homebrew
brew install ansible # Verify installation
ansible —version
### Linux (Ubuntu/Debian)bash
Add repository
sudo apt-add-repository ppa:ansible/ansible # Update and install
sudo apt update
sudo apt install ansible # Verify installation
ansible —version
### Using pip (All Platforms)bash
Install latest version
pip install ansible # Install specific version
pip install ansible==9.0.0 # Verify installation
ansible —version
```ini
# inventory.ini
[webservers]
web1.example.com
web2.example.com
web3.example.com [databases]
db1.example.com
db2.example.com [production:children]
webservers
databases [production:vars]
ansible_user=admin
ansible_ssh_private_key_file=~/.ssh/prod_key
``` **YAML Format:**
```yaml
# inventory.yml
all: children: webservers: hosts: web1.example.com: web2.example.com: web3.example.com: databases: hosts: db1.example.com: db2.example.com: production: children: webservers: databases: vars: ansible_user: admin ansible_ssh_private_key_file: ~/.ssh/prod_key
``` ### Playbooks Playbooks are YAML files that define automation workflows. They consist of plays, which map groups of hosts to roles or tasks. ### Modules Modules are units of code that Ansible executes. Ansible ships with thousands of modules for managing packages, files, services, cloud resources, and more. ### Roles Roles organize playbooks into reusable components with defined directory structures. ## Your First Ansible Playbook ### Ad-Hoc Commands Before writing playbooks, test Ansible with ad-hoc commands: ```bash
# Ping all hosts
ansible all -m ping -i inventory.ini # Check disk space
ansible webservers -m shell -a "df -h" -i inventory.ini # Install package
ansible databases -m apt -a "name=postgresql state=present" -b -i inventory.ini # Restart service
ansible webservers -m service -a "name=nginx state=restarted" -b -i inventory.ini
``` ### Basic Playbook Create `webserver.yml`: ```yaml
---
- name: Configure web servers hosts: webservers become: yes tasks: - name: Update apt cache apt: update_cache: yes cache_valid_time: 3600 - name: Install Nginx apt: name: nginx state: present - name: Start and enable Nginx service: name: nginx state: started enabled: yes - name: Copy website files copy: src: ./website/ dest: /var/www/html/ owner: www-data group: www-data mode: '0644' notify: Reload Nginx - name: Copy Nginx configuration template: src: templates/nginx.conf.j2 dest: /etc/nginx/sites-available/default notify: Reload Nginx handlers: - name: Reload Nginx service: name: nginx state: reloaded
``` Run the playbook: ```bash
ansible-playbook -i inventory.ini webserver.yml # Check mode (dry run)
ansible-playbook -i inventory.ini webserver.yml --check # Verbose output
ansible-playbook -i inventory.ini webserver.yml -v # Extra verbose
ansible-playbook -i inventory.ini webserver.yml -vvv
``` ## Working with Variables ### Defining Variables **In playbook:**
```yaml
---
- name: Deploy application hosts: webservers vars: app_name: myapp app_version: 1.0.0 app_port: 8080 tasks: - name: Display variables debug: msg: "Deploying {{ app_name }} version {{ app_version }} on port {{ app_port }}"
``` **In separate file:**
```yaml
# vars/main.yml
app_name: myapp
app_version: 1.0.0
app_port: 8080
database_host: db.example.com
database_name: myapp_db
``` ```yaml
# playbook.yml
---
- name: Deploy application hosts: webservers vars_files: - vars/main.yml tasks: - name: Configure application template: src: app-config.j2 dest: /etc/myapp/config.yml
``` ### Host and Group Variables ```
inventory/
├── hosts.ini
├── host_vars/
│ ├── web1.example.com.yml
│ └── db1.example.com.yml
└── group_vars/ ├── webservers.yml ├── databases.yml └── all.yml
``` ```yaml
# group_vars/webservers.yml
nginx_worker_processes: 4
nginx_worker_connections: 1024
php_version: "8.2" # host_vars/web1.example.com.yml
nginx_worker_processes: 8
server_role: primary
``` ### Using Facts Ansible automatically gathers facts about managed hosts: ```yaml
---
- name: Display facts hosts: all tasks: - name: Show OS family debug: msg: "This server runs {{ ansible_os_family }}" - name: Show IP address debug: msg: "IP address: {{ ansible_default_ipv4.address }}" - name: Show memory debug: msg: "Total memory: {{ ansible_memtotal_mb }} MB" - name: Install package based on OS package: name: "{{ 'httpd' if ansible_os_family == 'RedHat' else 'apache2' }}" state: present become: yes
``` ## Templates with Jinja2 Templates allow dynamic file generation: ```jinja2
{# templates/nginx.conf.j2 #}
server { listen {{ app_port }}; server_name {{ server_name }}; root {{ app_root }}; index index.html index.htm; location / { try_files $uri $uri/ =404; } {% if enable_ssl %} listen 443 ssl; ssl_certificate {{ ssl_cert_path }}; ssl_certificate_key {{ ssl_key_path }}; {% endif %} access_log /var/log/nginx/{{ app_name }}-access.log; error_log /var/log/nginx/{{ app_name }}-error.log;
}
``` ```yaml
# Use template in playbook
- name: Configure Nginx template: src: templates/nginx.conf.j2 dest: /etc/nginx/sites-available/{{ app_name }} vars: app_port: 80 server_name: example.com app_root: /var/www/html app_name: myapp enable_ssl: true ssl_cert_path: /etc/ssl/certs/example.com.crt ssl_key_path: /etc/ssl/private/example.com.key
``` ## Roles: Organizing Playbooks ### Creating a Role ```bash
# Create role structure
ansible-galaxy init nginx # Or manually create
mkdir -p roles/nginx/{tasks,handlers,templates,files,vars,defaults,meta}
``` Role structure: ```
roles/nginx/
├── tasks/
│ └── main.yml
├── handlers/
│ └── main.yml
├── templates/
│ └── nginx.conf.j2
├── files/
├── vars/
│ └── main.yml
├── defaults/
│ └── main.yml
├── meta/
│ └── main.yml
└── README.md
``` **roles/nginx/tasks/main.yml:**
```yaml
---
- name: Install Nginx apt: name: nginx state: present update_cache: yes - name: Copy Nginx configuration template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf notify: Restart Nginx - name: Ensure Nginx is running service: name: nginx state: started enabled: yes
``` **roles/nginx/handlers/main.yml:**
```yaml
---
- name: Restart Nginx service: name: nginx state: restarted
``` **roles/nginx/defaults/main.yml:**
```yaml
---
nginx_worker_processes: auto
nginx_worker_connections: 1024
nginx_keepalive_timeout: 65
``` **Using the role:**
```yaml
---
- name: Configure web servers hosts: webservers become: yes roles: - nginx - php - mysql
``` ## Advanced Playbook Techniques ### Conditionals ```yaml
---
- name: OS-specific tasks hosts: all become: yes tasks: - name: Install Apache on RedHat systems yum: name: httpd state: present when: ansible_os_family == "RedHat" - name: Install Apache on Debian systems apt: name: apache2 state: present when: ansible_os_family == "Debian" - name: Configure firewall for production firewalld: service: https permanent: yes state: enabled when: - environment == "production" - ansible_os_family == "RedHat"
``` ### Loops ```yaml
---
- name: Manage users and packages hosts: all become: yes tasks: - name: Create multiple users user: name: "{{ item.name }}" state: present groups: "{{ item.groups }}" loop: - { name: 'alice', groups: 'developers,sudo' } - { name: 'bob', groups: 'developers' } - { name: 'charlie', groups: 'ops,sudo' } - name: Install multiple packages apt: name: "{{ item }}" state: present loop: - git - vim - curl - htop - tmux - name: Create directories file: path: "/opt/{{ item }}" state: directory mode: '0755' loop: - app1 - app2 - app3
``` ### Blocks and Error Handling ```yaml
---
- name: Deploy with error handling hosts: webservers become: yes tasks: - name: Deployment tasks block: - name: Stop application service: name: myapp state: stopped - name: Deploy new version copy: src: /tmp/myapp-v2.0.0.tar.gz dest: /opt/myapp/ - name: Extract application unarchive: src: /opt/myapp/myapp-v2.0.0.tar.gz dest: /opt/myapp/ remote_src: yes - name: Start application service: name: myapp state: started rescue: - name: Rollback on failure copy: src: /opt/myapp/backup/ dest: /opt/myapp/ - name: Start application with old version service: name: myapp state: started - name: Send alert mail: to: ops@example.com subject: Deployment failed on {{ inventory_hostname }} body: Deployment failed and was rolled back always: - name: Cleanup temporary files file: path: /tmp/myapp-v2.0.0.tar.gz state: absent
``` ### Delegation and Local Actions ```yaml
---
- name: Deploy with load balancer management hosts: webservers serial: 1 # Deploy one server at a time tasks: - name: Remove from load balancer uri: url: "http://{{ loadbalancer_host }}/api/remove/{{ inventory_hostname }}" method: POST delegate_to: localhost - name: Wait for connections to drain wait_for: timeout: 30 - name: Deploy application copy: src: ./app.tar.gz dest: /opt/app/ notify: Restart app - name: Add back to load balancer uri: url: "http://{{ loadbalancer_host }}/api/add/{{ inventory_hostname }}" method: POST delegate_to: localhost
``` ## Real-World Examples ### Complete LAMP Stack Deployment ```yaml
---
- name: Deploy LAMP stack hosts: webservers become: yes vars: mysql_root_password: "{{ vault_mysql_root_password }}" db_name: wordpress_db db_user: wordpress_user db_password: "{{ vault_db_password }}" tasks: - name: Update apt cache apt: update_cache: yes cache_valid_time: 3600 - name: Install LAMP packages apt: name: - apache2 - mysql-server - php - php-mysql - libapache2-mod-php - python3-pymysql state: present - name: Start and enable services service: name: "{{ item }}" state: started enabled: yes loop: - apache2 - mysql - name: Set MySQL root password mysql_user: name: root password: "{{ mysql_root_password }}" login_unix_socket: /var/run/mysqld/mysqld.sock state: present - name: Create application database mysql_db: name: "{{ db_name }}" state: present login_user: root login_password: "{{ mysql_root_password }}" - name: Create database user mysql_user: name: "{{ db_user }}" password: "{{ db_password }}" priv: "{{ db_name }}.*:ALL" state: present login_user: root login_password: "{{ mysql_root_password }}" - name: Download WordPress get_url: url: https://wordpress.org/latest.tar.gz dest: /tmp/wordpress.tar.gz - name: Extract WordPress unarchive: src: /tmp/wordpress.tar.gz dest: /var/www/html/ remote_src: yes owner: www-data group: www-data - name: Configure WordPress template: src: wp-config.php.j2 dest: /var/www/html/wordpress/wp-config.php owner: www-data group: www-data mode: '0644' - name: Configure Apache virtual host template: src: wordpress.conf.j2 dest: /etc/apache2/sites-available/wordpress.conf notify: Restart Apache - name: Enable WordPress site command: a2ensite wordpress args: creates: /etc/apache2/sites-enabled/wordpress.conf notify: Restart Apache handlers: - name: Restart Apache service: name: apache2 state: restarted
``` ### Docker Container Deployment ```yaml
---
- name: Deploy Docker containers hosts: docker_hosts become: yes tasks: - name: Install Docker apt: name: - docker.io - docker-compose - python3-docker state: present update_cache: yes - name: Start Docker service service: name: docker state: started enabled: yes - name: Create application directory file: path: /opt/myapp state: directory mode: '0755' - name: Copy docker-compose file template: src: docker-compose.yml.j2 dest: /opt/myapp/docker-compose.yml - name: Deploy containers community.docker.docker_compose: project_src: /opt/myapp state: present pull: yes - name: Ensure containers are running community.docker.docker_container: name: "{{ item }}" state: started loop: - myapp_web - myapp_db - myapp_redis
``` ## Ansible Vault: Managing Secrets ### Creating Encrypted Files ```bash
# Create encrypted file
ansible-vault create secrets.yml # Edit encrypted file
ansible-vault edit secrets.yml # Encrypt existing file
ansible-vault encrypt vars/passwords.yml # Decrypt file
ansible-vault decrypt vars/passwords.yml # View encrypted file
ansible-vault view secrets.yml
``` **secrets.yml:**
```yaml
vault_mysql_root_password: SuperSecretPassword123!
vault_db_password: AnotherSecretPassword456!
vault_api_key: sk-1234567890abcdef
``` ### Using Vaulted Variables ```yaml
---
- name: Deploy with secrets hosts: databases become: yes vars_files: - secrets.yml tasks: - name: Set database password mysql_user: name: admin password: "{{ vault_mysql_root_password }}" state: present
``` Run with vault password: ```bash
# Prompt for password
ansible-playbook deploy.yml --ask-vault-pass # Use password file
ansible-playbook deploy.yml --vault-password-file ~/.vault_pass # Multiple vault IDs
ansible-playbook deploy.yml --vault-id prod@~/.vault_pass_prod --vault-id dev@~/.vault_pass_dev
``` ## Dynamic Inventory ### AWS EC2 Dynamic Inventory ```yaml
# aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions: - us-east-1 - us-west-2
filters: tag:Environment: production
keyed_groups: - key: tags.Role prefix: role - key: tags.Environment prefix: env
hostnames: - dns-name
compose: ansible_host: public_ip_address
``` Use dynamic inventory: ```bash
ansible-inventory -i aws_ec2.yml --graph
ansible-playbook -i aws_ec2.yml deploy.yml
``` ## Best Practices ### 1. Use Version Control ```bash
git init
echo "*.retry" >> .gitignore
echo "*.log" >> .gitignore
git add .
git commit -m "Initial Ansible project"
``` ### 2. Directory Structure ```
ansible-project/
├── ansible.cfg
├── inventory/
│ ├── production/
│ │ ├── hosts.ini
│ │ └── group_vars/
│ └── staging/
│ ├── hosts.ini
│ └── group_vars/
├── roles/
│ ├── common/
│ ├── webserver/
│ └── database/
├── playbooks/
│ ├── site.yml
│ ├── webservers.yml
│ └── databases.yml
├── group_vars/
│ └── all.yml
└── host_vars/
``` ### 3. Use Tags ```yaml
---
- name: Complete deployment hosts: webservers tasks: - name: Install packages apt: name: nginx state: present tags: [packages, nginx] - name: Configure application template: src: app.conf.j2 dest: /etc/app/app.conf tags: [config] - name: Deploy code git: repo: https://github.com/example/app.git dest: /opt/app tags: [deploy]
``` Run specific tags: ```bash
# Only install packages
ansible-playbook deploy.yml --tags packages # Skip deployment
ansible-playbook deploy.yml --skip-tags deploy # Multiple tags
ansible-playbook deploy.yml --tags "packages,config"
``` ### 4. Idempotency Ensure playbooks can run multiple times safely: ```yaml
# Bad: not idempotent
- name: Add line to file shell: echo "export PATH=/opt/bin:$PATH" >> ~/.bashrc # Good: idempotent
- name: Add line to file lineinfile: path: ~/.bashrc line: 'export PATH=/opt/bin:$PATH' state: present
``` ### 5. Use Check Mode Always test with check mode: ```bash
# Dry run
ansible-playbook deploy.yml --check # Show diff
ansible-playbook deploy.yml --check --diff
``` ## Troubleshooting ### Common Issues **Connection timeout:**
```bash
# Increase timeout
ansible-playbook deploy.yml -e "ansible_timeout=60" # Use different connection method
ansible-playbook deploy.yml -c paramiko
``` **Permission denied:**
```bash
# Specify SSH key
ansible-playbook deploy.yml --private-key=~/.ssh/id_rsa # Use password authentication
ansible-playbook deploy.yml --ask-pass
``` **Module not found:**
```bash
# Install collection
ansible-galaxy collection install community.general # List installed collections
ansible-galaxy collection list
``` ## Frequently Asked Questions ### Is Ansible faster than other configuration management tools? Ansible's agentless architecture makes it easier to set up but can be slower for large-scale deployments compared to agent-based tools like Puppet or Chef. Use strategies like `serial`, `async`, and proper task optimization to improve performance. ### Can Ansible manage Windows servers? Yes, Ansible supports Windows through WinRM: ```yaml
# inventory
[windows]
win1.example.com [windows:vars]
ansible_connection=winrm
ansible_user=Administrator
ansible_password=SecurePassword
ansible_winrm_transport=ntlm
ansible_winrm_server_cert_validation=ignore
``` ### How do I handle Ansible at scale? 1. Use Ansible Tower/AWX for centralized management
2. Implement dynamic inventory
3. Use strategies for parallel execution
4. use caching and fact gathering optimization
5. Consider Ansible pull mode for large deployments ### Should I use Ansible or Terraform? Use both! Terraform for infrastructure provisioning, Ansible for configuration management: ```bash
# Provision with Terraform
terraform apply # Configure with Ansible
ansible-playbook -i $(terraform output -raw inventory) configure.yml
``` ## Conclusion Ansible remains one of the most accessible and powerful automation tools in 2026. Its simplicity, combined with extensive module support and active community, makes it ideal for automating everything from simple server configuration to complex multi-tier application deployments. Start with simple playbooks, embrace roles for reusability, secure secrets with Vault, and gradually adopt advanced features as your automation needs grow. The investment in learning Ansible pays dividends in reduced manual work, improved consistency, and faster deployments. **Remember:**
- Keep playbooks simple and idempotent
- Use version control for all Ansible code
- Encrypt sensitive data with Ansible Vault
- Test with check mode before applying changes
- use the community and Ansible Galaxy for pre-built roles With Ansible in your DevOps toolkit, you're well-equipped to automate infrastructure and application management at any scale. Cite This Article
Use this citation when referencing this article in your own work.
APA MLA Chicago BibTeX
HostScout Team. (2026, January 14). Ansible Automation Guide 2026: From Basics to Advanced Playbooks. HostScout. https://hostscout.online/ansible-automation-guide/ HostScout Team. "Ansible Automation Guide 2026: From Basics to Advanced Playbooks." HostScout, 14 Jan. 2026, https://hostscout.online/ansible-automation-guide/. HostScout Team. "Ansible Automation Guide 2026: From Basics to Advanced Playbooks." HostScout. January 14, 2026. https://hostscout.online/ansible-automation-guide/. @online{ansible_automation_g_2026,
author = {HostScout Team},
title = {Ansible Automation Guide 2026: From Basics to Advanced Playbooks},
year = {2026},
url = {https://hostscout.online/ansible-automation-guide/},
urldate = {March 17, 2026},
organization = {HostScout}
}
Copy Citation