Ansible Linux Automation: Configuration Management Linux Mastery Series
Prerequisites
What is Ansible Linux Automation?
Ansible linux automation is an agentless, open-source IT automation platform that uses SSH to configure systems, deploy applications, and orchestrate complex workflows across multiple Linux servers. Unlike traditional scripting, Ansible uses simple YAML syntax called “playbooks” to define desired system states, making infrastructure management reproducible, scalable, and maintainable.
Quick Win Example – Deploy Apache to Multiple Servers:
---
- name: Install and configure Apache
hosts: webservers
become: yes
tasks:
- name: Install Apache
apt:
name: apache2
state: present
- name: Start Apache service
service:
name: apache2
state: started
enabled: yes
# Run the playbook
ansible-playbook apache-setup.yml
# Result: Apache installed and running on all webservers
# No agents, no complex scripts, just declarative automation
This immediately demonstrates Ansible’s power: manage dozens of servers with a single command, ensuring consistent configuration across your entire infrastructure.
Table of Contents
- How Does Ansible Linux Automation Work?
- What Are the Core Components of Ansible?
- How to Install and Configure Ansible on Linux?
- What is an Ansible Inventory and How to Create One?
- How to Write Your First Ansible Playbook?
- What Are Ansible Modules and How to Use Them?
- How to Use Ansible Roles for Reusability?
- What is Idempotency and Why Does It Matter?
- How to Manage Variables and Facts in Ansible?
- Best Practices for Ansible Linux Automation
- Frequently Asked Questions
- Troubleshooting Common Ansible Issues
How Does Ansible Linux Automation Work?
Ansible operates on a simple yet powerful architecture: a control node (your management machine) connects to managed nodes (target servers) via SSH, executes tasks defined in playbooks, and ensures systems match the desired state. Consequently, this agentless approach eliminates the overhead of installing and maintaining software on every server.
Ansible Architecture Overview
βββββββββββββββββββββββββββββββββββββββ
β Control Node (Your Machine) β
β - Ansible CLI β
β - Python 3.8+ β
β - Playbooks & Roles β
ββββββββββββββββ¬βββββββββββββββββββββββ
β SSH Connection
β
ββββββββ΄βββββββ¬βββββββββββ¬βββββββββ
β β β β
βββββββββΌβββββββ ββββββΌβββββ ββββΌββββββ ββΌβββββββββ
β Web Server 1 β β DB β β Cache β β Load β
β SSH Port 22 β β Server β β Server β β Balancerβ
β Python 3 β β β β β β β
ββββββββββββββββ βββββββββββ ββββββββββ βββββββββββ
Managed Nodes (No Ansible Agent Required)
Execution Flow
# 1. Read inventory file
ansible all --list-hosts
# 2. Parse playbook YAML
ansible-playbook site.yml --syntax-check
# 3. Establish SSH connections
ansible all -m ping
# 4. Execute modules on remote hosts
ansible webservers -m command -a "uptime"
# 5. Gather results and report
ansible-playbook site.yml -v
Key Advantages:
Feature | Benefit |
---|---|
Agentless | No software installation on managed nodes |
SSH-based | Uses existing secure infrastructure |
Declarative | Define “what” not “how” |
Idempotent | Safe to run repeatedly |
Extensible | 3,000+ built-in modules |
Furthermore, the System Services with systemd guide complements Ansible automation by explaining service management on target systems.
What Are the Core Components of Ansible?
Understanding Ansible’s building blocks enables you to architect effective ansible linux automation solutions. Therefore, let’s examine each component in detail.
1. Control Node
Your management machine where Ansible runs:
# Check if Ansible is installed
ansible --version
# Typical output:
# ansible [core 2.15.4]
# config file = /etc/ansible/ansible.cfg
# configured module search path = ['/home/user/.ansible/plugins/modules']
# ansible python module location = /usr/lib/python3/dist-packages/ansible
# python version = 3.11.2
# Requirements
python3 --version # Python 3.8+
ssh -V # OpenSSH client
2. Managed Nodes
Target servers that Ansible configures:
# Minimum requirements on managed nodes:
# - Python 3.5+ (usually pre-installed)
# - SSH server running
# - SSH key-based authentication (recommended)
# Verify SSH access
ssh user@managed-node.example.com "python3 --version"
3. Inventory
Lists of servers organized into groups:
# /etc/ansible/hosts (INI format)
[webservers]
web1.example.com ansible_host=192.168.1.10 web2.example.com ansible_host=192.168.1.11
[databases]
db1.example.com ansible_port=2222 db2.example.com
[loadbalancers]
lb1.example.com # Group variables
[webservers:vars]
ansible_user=deploy http_port=80
YAML Inventory Format:
# inventory.yml
all:
children:
webservers:
hosts:
web1.example.com:
ansible_host: 192.168.1.10
web2.example.com:
ansible_host: 192.168.1.11
vars:
http_port: 80
databases:
hosts:
db1.example.com:
ansible_port: 2222
4. Playbooks
YAML files defining tasks and configurations:
---
# site.yml - Complete infrastructure playbook
- name: Configure web servers
hosts: webservers
become: yes
tasks:
- name: Install Nginx
apt:
name: nginx
state: present
update_cache: yes
- name: Deploy website content
copy:
src: website/
dest: /var/www/html/
owner: www-data
group: www-data
- name: Ensure Nginx is running
service:
name: nginx
state: started
enabled: yes
5. Modules
Reusable units that perform specific tasks:
# Package management
ansible webservers -m apt -a "name=nginx state=present"
# File operations
ansible webservers -m copy -a "src=/local/file dest=/remote/file"
# Service management
ansible webservers -m service -a "name=nginx state=restarted"
# Command execution
ansible webservers -m command -a "uptime"
# User management
ansible webservers -m user -a "name=deploy state=present"
6. Roles
Organized, reusable collections of tasks, variables, and files:
roles/
webserver/
βββ tasks/
β βββ main.yml
βββ handlers/
β βββ main.yml
βββ templates/
β βββ nginx.conf.j2
βββ files/
β βββ index.html
βββ vars/
β βββ main.yml
βββ defaults/
β βββ main.yml
βββ meta/
βββ main.yml
According to the Ansible Documentation, roles provide the best practice structure for organizing complex automation projects.
How to Install and Configure Ansible on Linux?
Installing ansible linux automation tools varies by distribution, but the process remains straightforward. Moreover, proper initial configuration ensures smooth operation across your infrastructure.
Ubuntu/Debian
# Update package index
sudo apt update
# Install software-properties-common
sudo apt install -y software-properties-common
# Add Ansible PPA repository
sudo add-apt-repository --yes --update ppa:ansible/ansible
# Install Ansible
sudo apt install -y ansible
# Verify installation
ansible --version
RHEL/CentOS/Rocky Linux
# Enable EPEL repository
sudo dnf install -y epel-release
# Install Ansible
sudo dnf install -y ansible
# Verify installation
ansible --version
Fedora
# Ansible is in default repositories
sudo dnf install -y ansible
# Install additional Python dependencies
sudo dnf install -y python3-pip python3-argcomplete
# Enable shell completion
sudo activate-global-python-argcomplete
pip (Universal Method)
# Ensure pip is installed
sudo apt install -y python3-pip # Debian/Ubuntu
# OR
sudo dnf install -y python3-pip # RHEL/Fedora
# Install Ansible using pip
pip3 install ansible
# Add to PATH if needed
echo 'export PATH=$PATH:$HOME/.local/bin' >> ~/.bashrc
source ~/.bashrc
# Verify
ansible --version
Initial Configuration
# Create Ansible configuration directory
mkdir -p ~/.ansible
# Create custom ansible.cfg
cat > ~/.ansible/ansible.cfg << 'EOF'
[defaults]
inventory = ./inventory host_key_checking = False retry_files_enabled = False gathering = smart fact_caching = jsonfile fact_caching_connection = /tmp/ansible_facts fact_caching_timeout = 3600
[privilege_escalation]
become = True become_method = sudo become_user = root become_ask_pass = False
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s pipelining = True EOF # Set environment variable to use this config echo ‘export ANSIBLE_CONFIG=~/.ansible/ansible.cfg’ >> ~/.bashrc source ~/.bashrc
SSH Key Setup for Passwordless Authentication
# Generate SSH key pair (if not exists)
if [ ! -f ~/.ssh/id_rsa ]; then
ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa -N ""
fi
# Copy public key to managed nodes
ssh-copy-id user@web1.example.com
ssh-copy-id user@web2.example.com
ssh-copy-id user@db1.example.com
# Test SSH connection
ssh user@web1.example.com "echo 'SSH connection successful'"
# Test Ansible connectivity
ansible all -m ping
Ansible Configuration Priority
Ansible searches for configuration in this order:
# 1. ANSIBLE_CONFIG environment variable
export ANSIBLE_CONFIG=/path/to/ansible.cfg
# 2. ansible.cfg in current directory
./ansible.cfg
# 3. ~/.ansible.cfg in home directory
~/.ansible.cfg
# 4. /etc/ansible/ansible.cfg system-wide
/etc/ansible/ansible.cfg
# View current configuration
ansible-config dump --only-changed
The Linux Foundation Training offers comprehensive courses on configuration management and automation best practices.
What is an Ansible Inventory and How to Create One?
The inventory defines which servers Ansible manages and how to connect to them. Consequently, a well-structured inventory simplifies playbook development and execution.
Static Inventory – INI Format
# inventory/production.ini
# Ungrouped hosts
standalone.example.com
# Web servers group
[webservers]
web1.example.com web2.example.com ansible_host=192.168.1.11 web3.example.com ansible_port=2222 # Database servers
[databases]
db-primary.example.com ansible_user=dbadmin db-replica1.example.com db-replica2.example.com # Application servers
[appservers]
app[01:10].example.com # Expands to app01-app10 # Load balancers
[loadbalancers]
lb1.example.com lb2.example.com # Group of groups
[production:children]
webservers databases appservers loadbalancers # Variables for all production servers
[production:vars]
ansible_user=deploy ansible_python_interpreter=/usr/bin/python3 env=production # Variables specific to webservers
[webservers:vars]
http_port=80 max_clients=200 # Variables for databases
[databases:vars]
mysql_port=3306 backup_enabled=true
Static Inventory – YAML Format
# inventory/production.yml
all:
hosts:
standalone.example.com:
children:
production:
children:
webservers:
hosts:
web1.example.com:
web2.example.com:
ansible_host: 192.168.1.11
web3.example.com:
ansible_port: 2222
vars:
http_port: 80
max_clients: 200
databases:
hosts:
db-primary.example.com:
ansible_user: dbadmin
role: primary
db-replica1.example.com:
role: replica
db-replica2.example.com:
role: replica
vars:
mysql_port: 3306
backup_enabled: true
appservers:
hosts:
app[01:10].example.com:
loadbalancers:
hosts:
lb1.example.com:
lb2.example.com:
vars:
ansible_user: deploy
ansible_python_interpreter: /usr/bin/python3
env: production
Dynamic Inventory
#!/usr/bin/env python3
# inventory/dynamic_inventory.py
"""
Dynamic inventory script for AWS EC2 instances
"""
import json
import boto3
def get_inventory():
ec2 = boto3.client('ec2', region_name='us-east-1')
inventory = {
'all': {
'hosts': [],
'vars': {}
},
'webservers': {
'hosts': [],
'vars': {'http_port': 80}
},
'_meta': {
'hostvars': {}
}
}
# Query EC2 instances
response = ec2.describe_instances(
Filters=[{'Name': 'instance-state-name', 'Values': ['running']}]
)
for reservation in response['Reservations']:
for instance in reservation['Instances']:
# Get instance details
instance_id = instance['InstanceId']
public_ip = instance.get('PublicIpAddress', '')
private_ip = instance.get('PrivateIpAddress', '')
# Get tags
tags = {tag['Key']: tag['Value'] for tag in instance.get('Tags', [])}
name = tags.get('Name', instance_id)
role = tags.get('Role', 'undefined')
# Add to inventory
inventory['all']['hosts'].append(name)
# Add to role-specific group
if role not in inventory:
inventory[role] = {'hosts': [], 'vars': {}}
inventory[role]['hosts'].append(name)
# Add host vars
inventory['_meta']['hostvars'][name] = {
'ansible_host': public_ip or private_ip,
'instance_id': instance_id,
'instance_type': instance['InstanceType'],
'tags': tags
}
return inventory
if __name__ == '__main__':
print(json.dumps(get_inventory(), indent=2))
# Make dynamic inventory executable
chmod +x inventory/dynamic_inventory.py
# Test dynamic inventory
./inventory/dynamic_inventory.py
# Use with ansible
ansible -i inventory/dynamic_inventory.py all --list-hosts
# Use in playbook
ansible-playbook -i inventory/dynamic_inventory.py site.yml
Inventory Commands and Queries
# List all hosts
ansible all --list-hosts
# List hosts in specific group
ansible webservers --list-hosts
# Show inventory graph
ansible-inventory --graph
# Show inventory in JSON
ansible-inventory --list
# Show host variables
ansible-inventory --host web1.example.com
# Test connectivity
ansible all -m ping
# Run ad-hoc command
ansible webservers -m command -a "uptime"
# Check which hosts match pattern
ansible "web*" --list-hosts
ansible "~(web|app).*" --list-hosts # Regex pattern
Inventory Best Practices
# Organize inventory by environment
inventory/
βββ production/
β βββ hosts.yml
β βββ group_vars/
β βββ all.yml
β βββ webservers.yml
β βββ databases.yml
βββ staging/
β βββ hosts.yml
β βββ group_vars/
β βββ all.yml
βββ development/
βββ hosts.yml
βββ group_vars/
βββ all.yml
# Use inventory with specific environment
ansible-playbook -i inventory/production site.yml
ansible-playbook -i inventory/staging site.yml
Additionally, the Bash Scripting Basics guide explains shell scripting concepts useful for creating dynamic inventory scripts.
How to Write Your First Ansible Playbook?
Playbooks are the heart of ansible linux automation, defining tasks in simple YAML syntax. Therefore, mastering playbook creation enables you to automate complex infrastructure management.
Basic Playbook Structure
---
# playbook.yml - Basic structure
- name: Descriptive play name
hosts: target_group
become: yes # Run with sudo
vars:
variable_name: value
tasks:
- name: Descriptive task name
module_name:
parameter: value
Complete Web Server Deployment Playbook
---
# webserver-deploy.yml
- name: Deploy and configure web application
hosts: webservers
become: yes
vars:
app_name: myapp
app_version: "1.0.0"
app_user: www-data
document_root: "/var/www/{{ app_name }}"
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install required packages
apt:
name:
- nginx
- python3-pip
- git
state: present
- name: Create application directory
file:
path: "{{ document_root }}"
state: directory
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: '0755'
- name: Deploy application files
copy:
src: "app/"
dest: "{{ document_root }}/"
owner: "{{ app_user }}"
group: "{{ app_user }}"
notify: Restart nginx
- name: Configure Nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/sites-available/{{ app_name }}
notify: Reload nginx
- name: Enable Nginx site
file:
src: "/etc/nginx/sites-available/{{ app_name }}"
dest: "/etc/nginx/sites-enabled/{{ app_name }}"
state: link
notify: Reload nginx
- name: Ensure Nginx is running
service:
name: nginx
state: started
enabled: yes
- name: Open firewall for HTTP
ufw:
rule: allow
port: '80'
proto: tcp
handlers:
- name: Restart nginx
service:
name: nginx
state: restarted
- name: Reload nginx
service:
name: nginx
state: reloaded
Nginx Configuration Template
{# templates/nginx.conf.j2 #}
server {
listen 80;
server_name {{ ansible_fqdn }};
root {{ document_root }};
index index.html index.htm;
access_log /var/log/nginx/{{ app_name }}_access.log;
error_log /var/log/nginx/{{ app_name }}_error.log;
location / {
try_files $uri $uri/ =404;
}
location /api/ {
proxy_pass http://localhost:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Running Playbooks
# Basic execution
ansible-playbook webserver-deploy.yml
# Dry run (check mode)
ansible-playbook webserver-deploy.yml --check
# Show differences
ansible-playbook webserver-deploy.yml --check --diff
# Limit to specific hosts
ansible-playbook webserver-deploy.yml --limit web1.example.com
# Use specific inventory
ansible-playbook -i inventory/production webserver-deploy.yml
# Increase verbosity
ansible-playbook webserver-deploy.yml -v # Basic
ansible-playbook webserver-deploy.yml -vv # More detail
ansible-playbook webserver-deploy.yml -vvv # Debug level
# Start at specific task
ansible-playbook webserver-deploy.yml --start-at-task="Configure Nginx"
# Use tags
ansible-playbook webserver-deploy.yml --tags "configuration"
ansible-playbook webserver-deploy.yml --skip-tags "deployment"
Multi-Play Playbook
---
# site.yml - Complete infrastructure deployment
- name: Configure load balancers
hosts: loadbalancers
become: yes
roles:
- haproxy
- keepalived
- name: Setup web servers
hosts: webservers
become: yes
roles:
- common
- nginx
- application
- name: Configure databases
hosts: databases
become: yes
serial: 1 # One at a time
roles:
- common
- mysql
- replication
- name: Deploy monitoring
hosts: all
become: yes
roles:
- prometheus-node-exporter
- filebeat
Conditional Execution
---
- name: OS-specific package installation
hosts: all
become: yes
tasks:
- name: Install Apache on Debian/Ubuntu
apt:
name: apache2
state: present
when: ansible_os_family == "Debian"
- name: Install Apache on RHEL/CentOS
yum:
name: httpd
state: present
when: ansible_os_family == "RedHat"
- name: Ensure service is running
service:
name: "{{ 'apache2' if ansible_os_family == 'Debian' else 'httpd' }}"
state: started
enabled: yes
Loops in Playbooks
---
- name: Create multiple users
hosts: all
become: yes
vars:
users:
- name: alice
uid: 1001
groups: ['sudo', 'developers']
- name: bob
uid: 1002
groups: ['developers']
- name: charlie
uid: 1003
groups: ['operators']
tasks:
- name: Create user accounts
user:
name: "{{ item.name }}"
uid: "{{ item.uid }}"
groups: "{{ item.groups }}"
state: present
shell: /bin/bash
loop: "{{ users }}"
- name: Install packages
apt:
name: "{{ item }}"
state: present
loop:
- vim
- git
- htop
- curl
- wget
Moreover, the Regular Expressions in Linux guide helps with pattern matching in Ansible filters and conditionals.
What Are Ansible Modules and How to Use Them?
Modules are the workhorses of ansible linux automation, providing idempotent operations for virtually every administrative task. Furthermore, understanding commonly used modules accelerates your automation development.
Essential System Modules
---
- name: System administration tasks
hosts: all
become: yes
tasks:
# Package management (apt)
- name: Install packages on Debian/Ubuntu
apt:
name:
- nginx
- python3-pip
- git
state: present
update_cache: yes
# Package management (yum/dnf)
- name: Install packages on RHEL/CentOS
yum:
name:
- httpd
- python3
- git
state: latest
# Service management
- name: Ensure service is running
service:
name: nginx
state: started
enabled: yes
# systemd module (more advanced)
- name: Manage systemd service
systemd:
name: nginx
state: restarted
enabled: yes
daemon_reload: yes
# User management
- name: Create user account
user:
name: deploy
uid: 1500
groups: sudo,www-data
shell: /bin/bash
create_home: yes
state: present
# Group management
- name: Create group
group:
name: developers
gid: 2000
state: present
# Cron jobs
- name: Schedule backup job
cron:
name: "Daily backup"
minute: "0"
hour: "2"
job: "/usr/local/bin/backup.sh"
user: root
File and Directory Modules
---
- name: File system operations
hosts: all
become: yes
tasks:
# Copy files
- name: Copy configuration file
copy:
src: files/app.conf
dest: /etc/app/app.conf
owner: root
group: root
mode: '0644'
backup: yes
# Template files (Jinja2)
- name: Deploy config from template
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
validate: 'nginx -t -c %s'
notify: Reload nginx
# Create directories
- name: Ensure directory exists
file:
path: /var/www/myapp
state: directory
owner: www-data
group: www-data
mode: '0755'
recurse: yes
# Create symbolic links
- name: Create symlink
file:
src: /etc/nginx/sites-available/myapp
dest: /etc/nginx/sites-enabled/myapp
state: link
# Set file permissions
- name: Set permissions
file:
path: /opt/scripts/deploy.sh
mode: '0755'
# Fetch files from remote
- name: Fetch log files
fetch:
src: /var/log/application.log
dest: logs/{{ inventory_hostname }}/
flat: yes
# Synchronize directories
- name: Sync files with rsync
synchronize:
src: /local/path/
dest: /remote/path/
delete: yes
recursive: yes
Command Execution Modules
---
- name: Execute commands
hosts: all
become: yes
tasks:
# Simple command
- name: Check disk usage
command: df -h
register: disk_usage
- name: Display disk usage
debug:
var: disk_usage.stdout_lines
# Shell command (with pipes and redirects)
- name: Complex shell command
shell: |
ps aux | grep nginx | wc -l
register: nginx_processes
# Raw command (no Python required)
- name: Install Python on fresh system
raw: apt-get install -y python3
when: ansible_python_interpreter is not defined
# Script execution
- name: Run local script remotely
script: scripts/setup.sh
args:
creates: /etc/app/configured
# Expect module (interactive commands)
- name: Change password interactively
expect:
command: passwd deploy
responses:
(?i)password: "{{ new_password }}"
Network Modules
---
- name: Network configuration
hosts: all
become: yes
tasks:
# Firewall (UFW)
- name: Configure firewall rules
ufw:
rule: allow
port: "{{ item }}"
proto: tcp
loop:
- '22'
- '80'
- '443'
# Firewall (firewalld)
- name: Open port in firewalld
firewalld:
port: 8080/tcp
permanent: yes
state: enabled
immediate: yes
# Network interface configuration
- name: Configure static IP
template:
src: network-interface.j2
dest: /etc/network/interfaces
notify: Restart networking
# DNS configuration
- name: Set DNS servers
lineinfile:
path: /etc/resolv.conf
line: "nameserver {{ item }}"
loop:
- 8.8.8.8
- 8.8.4.4
# Download files
- name: Download file from URL
get_url:
url: https://example.com/file.tar.gz
dest: /tmp/file.tar.gz
checksum: sha256:abc123...
Database Modules
---
- name: Database management
hosts: databases
become: yes
tasks:
# MySQL database
- name: Create MySQL database
mysql_db:
name: myapp_db
state: present
encoding: utf8mb4
collation: utf8mb4_unicode_ci
# MySQL user
- name: Create MySQL user
mysql_user:
name: myapp_user
password: "{{ db_password }}"
priv: 'myapp_db.*:ALL'
host: '%'
state: present
# PostgreSQL database
- name: Create PostgreSQL database
postgresql_db:
name: myapp_db
encoding: UTF-8
state: present
# PostgreSQL user
- name: Create PostgreSQL user
postgresql_user:
name: myapp_user
password: "{{ db_password }}"
db: myapp_db
priv: ALL
state: present
# Execute SQL
- name: Run SQL script
mysql_db:
name: myapp_db
state: import
target: /tmp/schema.sql
Cloud Modules (AWS Example)
---
- name: AWS infrastructure
hosts: localhost
gather_facts: no
tasks:
# EC2 instance
- name: Launch EC2 instance
amazon.aws.ec2_instance:
name: web-server-01
instance_type: t3.micro
image_id: ami-0c55b159cbfafe1f0
region: us-east-1
key_name: my-key-pair
vpc_subnet_id: subnet-abc123
security_group: web-sg
tags:
Environment: production
Role: webserver
state: running
# S3 bucket
- name: Create S3 bucket
amazon.aws.s3_bucket:
name: my-app-backups
region: us-east-1
versioning: yes
state: present
# RDS instance
- name: Create RDS database
amazon.aws.rds_instance:
db_instance_identifier: myapp-db
engine: mysql
db_instance_class: db.t3.micro
allocated_storage: 20
master_username: admin
master_user_password: "{{ rds_password }}"
state: present
Module Documentation
# List all modules
ansible-doc -l
# View module documentation
ansible-doc apt
ansible-doc copy
ansible-doc template
# Search for modules
ansible-doc -l | grep mysql
# View module examples
ansible-doc -s user
The Ansible Module Index provides comprehensive documentation for all 3,000+ built-in modules.
How to Use Ansible Roles for Reusability?
Roles organize ansible linux automation code into reusable, shareable components. Consequently, well-designed roles accelerate development and improve maintainability across projects.
Role Directory Structure
# Create role skeleton
ansible-galaxy init webserver
# Resulting structure:
webserver/
βββ README.md
βββ defaults/
β βββ main.yml # Default variables (lowest priority)
βββ files/
β βββ index.html # Static files to copy
βββ handlers/
β βββ main.yml # Handlers (triggered by notify)
βββ meta/
β βββ main.yml # Role metadata and dependencies
βββ tasks/
β βββ main.yml # Main task list
βββ templates/
β βββ nginx.conf.j2 # Jinja2 templates
βββ tests/
β βββ inventory
β βββ test.yml
βββ vars/
βββ main.yml # Role variables (high priority)
Complete Web Server Role
roles/webserver/defaults/main.yml
---
# Default variables
webserver_port: 80
webserver_user: www-data
webserver_document_root: /var/www/html
webserver_max_clients: 100
webserver_packages:
- nginx
- python3-pip
roles/webserver/vars/main.yml
---
# Role-specific variables
nginx_config_path: /etc/nginx
nginx_sites_available: "{{ nginx_config_path }}/sites-available"
nginx_sites_enabled: "{{ nginx_config_path }}/sites-enabled"
roles/webserver/tasks/main.yml
---
# Main tasks file
- name: Include OS-specific variables
include_vars: "{{ ansible_os_family }}.yml"
- name: Install web server packages
apt:
name: "{{ webserver_packages }}"
state: present
update_cache: yes
when: ansible_os_family == "Debian"
- name: Create document root
file:
path: "{{ webserver_document_root }}"
state: directory
owner: "{{ webserver_user }}"
group: "{{ webserver_user }}"
mode: '0755'
- name: Deploy default index page
copy:
src: index.html
dest: "{{ webserver_document_root }}/index.html"
owner: "{{ webserver_user }}"
group: "{{ webserver_user }}"
mode: '0644'
- name: Configure Nginx
template:
src: nginx.conf.j2
dest: "{{ nginx_config_path }}/nginx.conf"
validate: 'nginx -t -c %s'
notify: Reload nginx
- name: Ensure Nginx is running
service:
name: nginx
state: started
enabled: yes
- name: Configure firewall
ufw:
rule: allow
port: "{{ webserver_port }}"
proto: tcp
roles/webserver/handlers/main.yml
---
# Handlers
- name: Reload nginx
service:
name: nginx
state: reloaded
- name: Restart nginx
service:
name: nginx
state: restarted
roles/webserver/templates/nginx.conf.j2
user {{ webserver_user }};
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections {{ webserver_max_clients }};
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
gzip on;
include {{ nginx_sites_enabled }}/*;
}
roles/webserver/meta/main.yml
---
galaxy_info:
author: Your Name
description: Nginx web server configuration
company: LinuxTips.pro
license: MIT
min_ansible_version: 2.9
platforms:
- name: Ubuntu
versions:
- focal
- jammy
- name: Debian
versions:
- bullseye
- bookworm
galaxy_tags:
- web
- nginx
- webserver
dependencies:
- role: common
vars:
ntp_enabled: true
Using Roles in Playbooks
---
# site.yml - Using roles
- name: Configure web infrastructure
hosts: webservers
become: yes
roles:
- role: common
tags: ['common']
- role: webserver
tags: ['web']
webserver_port: 8080
webserver_max_clients: 200
- role: ssl
tags: ['ssl']
when: ssl_enabled | default(false)
# Alternative syntax with tasks
- name: Mixed approach
hosts: webservers
become: yes
pre_tasks:
- name: Update apt cache
apt:
update_cache: yes
roles:
- webserver
post_tasks:
- name: Verify web server is responding
uri:
url: http://localhost
return_content: yes
register: result
failed_when: "'Welcome' not in result.content"
Role Dependencies
# roles/application/meta/main.yml
---
dependencies:
- role: common
- role: webserver
webserver_port: 8080
- role: database
db_name: myapp
Installing Roles from Ansible Galaxy
# Install role from Galaxy
ansible-galaxy install geerlingguy.nginx
# Install specific version
ansible-galaxy install geerlingguy.nginx,2.8.0
# Install from requirements file
# requirements.yml
---
- src: geerlingguy.nginx
version: 2.8.0
- src: geerlingguy.mysql
- src: https://github.com/username/ansible-role-custom
name: custom-role
# Install all requirements
ansible-galaxy install -r requirements.yml
# Install to specific path
ansible-galaxy install -r requirements.yml -p ./roles/
# List installed roles
ansible-galaxy list
# Remove role
ansible-galaxy remove geerlingguy.nginx
The Ansible Galaxy hosts thousands of community-contributed roles for common automation tasks.
What is Idempotency and Why Does It Matter?
Idempotency is a core principle of ansible linux automation ensuring that running the same playbook multiple times produces the same result. Therefore, understanding and maintaining idempotency prevents configuration drift and enables safe reexecution.
Understanding Idempotent Operations
---
- name: Idempotency examples
hosts: all
become: yes
tasks:
# IDEMPOTENT: Package installation
- name: Ensure nginx is installed
apt:
name: nginx
state: present
# Result: Installs if missing, no change if present
# IDEMPOTENT: Service state
- name: Ensure nginx is running
service:
name: nginx
state: started
enabled: yes
# Result: Starts if stopped, no change if running
# IDEMPOTENT: File content
- name: Ensure configuration line exists
lineinfile:
path: /etc/nginx/nginx.conf
line: 'worker_processes auto;'
regexp: '^worker_processes'
# Result: Adds if missing, no change if present
# NOT IDEMPOTENT: Append without check
- name: BAD - Appends every time
shell: echo "log entry" >> /var/log/app.log
# Problem: Creates duplicate entries on each run
# IDEMPOTENT FIX: Use creates parameter
- name: GOOD - Run only once
shell: echo "initialized" > /var/log/app.log
args:
creates: /var/log/app.log
# Result: Runs once, skips on subsequent runs
Making Commands Idempotent
---
- name: Idempotent command patterns
hosts: all
become: yes
tasks:
# Use creates parameter
- name: Download file once
command: wget https://example.com/file.tar.gz
args:
chdir: /tmp
creates: /tmp/file.tar.gz
# Use removes parameter
- name: Clean up old files
command: rm -f /tmp/old-file
args:
removes: /tmp/old-file
# Check before executing
- name: Check if already configured
stat:
path: /etc/app/configured
register: configured
- name: Run configuration script
script: configure-app.sh
when: not configured.stat.exists
- name: Create marker file
file:
path: /etc/app/configured
state: touch
when: not configured.stat.exists
Testing Idempotency
# Run playbook first time
ansible-playbook site.yml
# Expected output:
# TASK [Install nginx] *******
# changed: [web1] β Changes made
# Run playbook second time
ansible-playbook site.yml
# Expected output:
# TASK [Install nginx] *******
# ok: [web1] β No changes (idempotent)
# Check for changes
ansible-playbook site.yml --check --diff
# Count changes
ansible-playbook site.yml | grep -c changed=
# Should be 0 on second run for idempotent playbook
Non-Idempotent Patterns to Avoid
---
# ANTI-PATTERNS - DO NOT USE
- name: BAD - Appends every run
shell: echo "export PATH=$PATH:/opt/bin" >> ~/.bashrc
- name: BAD - Creates duplicates
lineinfile:
path: /etc/hosts
line: "192.168.1.10 server1"
# Missing regexp to check if exists
- name: BAD - Always changes
command: date > /tmp/timestamp.txt
- name: BAD - Increments value
shell: expr $(cat /tmp/counter) + 1 > /tmp/counter
# CORRECT PATTERNS
- name: GOOD - Idempotent PATH addition
lineinfile:
path: ~/.bashrc
line: 'export PATH=$PATH:/opt/bin'
regexp: '^export PATH=.*:/opt/bin'
- name: GOOD - Prevents duplicates
lineinfile:
path: /etc/hosts
line: "192.168.1.10 server1"
regexp: '^192\.168\.1\.10'
- name: GOOD - Updates only if changed
copy:
content: "{{ ansible_date_time.iso8601 }}"
dest: /tmp/timestamp.txt
# Only changes if content differs
- name: GOOD - Sets value idempotently
copy:
content: "10"
dest: /tmp/counter
# Sets to 10 regardless of current value
Similarly, the Error Handling in Bash Scripts guide demonstrates defensive programming patterns applicable to Ansible task design.
How to Manage Variables and Facts in Ansible?
Variables and facts provide dynamic data to ansible linux automation playbooks, enabling flexible, reusable configurations. Consequently, mastering variable management improves playbook adaptability across environments.
Variable Definition Locations
# 1. Playbook variables
---
- name: Using playbook variables
hosts: all
vars:
app_name: myapp
app_version: "1.0.0"
tasks:
- name: Deploy application
debug:
msg: "Deploying {{ app_name }} version {{ app_version }}"
# 2. Inventory variables (host_vars)
# host_vars/web1.example.com.yml
---
ansible_host: 192.168.1.10
http_port: 8080
ssl_enabled: true
# 3. Inventory variables (group_vars)
# group_vars/webservers.yml
---
nginx_worker_processes: 4
nginx_max_clients: 100
# 4. Role variables
# roles/webserver/vars/main.yml
---
default_document_root: /var/www/html
# 5. Extra variables (command line)
ansible-playbook site.yml -e "env=production debug=false"
Variable Precedence (Lowest to Highest)
1. role defaults (defaults/main.yml)
2. inventory file or script group vars
3. inventory group_vars/all
4. playbook group_vars/all
5. inventory group_vars/*
6. playbook group_vars/*
7. inventory file or script host vars
8. inventory host_vars/*
9. playbook host_vars/*
10. host facts / cached set_facts
11. play vars
12. play vars_prompt
13. play vars_files
14. role vars (vars/main.yml)
15. block vars (only for tasks in block)
16. task vars (only for the task)
17. include_vars
18. set_facts / registered vars
19. role (and include_role) params
20. include params
21. extra vars (-e in CLI)
Working with Facts
---
- name: Using Ansible facts
hosts: all
gather_facts: yes
tasks:
- name: Display all facts
debug:
var: ansible_facts
- name: Show specific facts
debug:
msg: |
Hostname: {{ ansible_hostname }}
OS Family: {{ ansible_os_family }}
Distribution: {{ ansible_distribution }}
Version: {{ ansible_distribution_version }}
Python: {{ ansible_python_version }}
IP Address: {{ ansible_default_ipv4.address }}
CPU Cores: {{ ansible_processor_cores }}
Memory: {{ ansible_memtotal_mb }} MB
- name: OS-specific task
apt:
name: apache2
state: present
when: ansible_os_family == "Debian"
- name: Memory-based decision
debug:
msg: "High memory system"
when: ansible_memtotal_mb >= 8192
Custom Facts
# Create custom fact script
sudo mkdir -p /etc/ansible/facts.d
# /etc/ansible/facts.d/application.fact
sudo tee /etc/ansible/facts.d/application.fact << 'EOF'
#!/bin/bash
cat << JSON
{
"app_name": "myapp",
"app_version": "1.0.0",
"environment": "production",
"last_deployment": "$(date -I)"
}
JSON
EOF
sudo chmod +x /etc/ansible/facts.d/application.fact
---
- name: Using custom facts
hosts: all
gather_facts: yes
tasks:
- name: Show custom facts
debug:
msg: "{{ ansible_local.application }}"
- name: Use custom fact in task
debug:
msg: "App: {{ ansible_local.application.app_name }}"
Registered Variables
---
- name: Using registered variables
hosts: all
tasks:
- name: Check if file exists
stat:
path: /etc/app/config.yml
register: config_file
- name: Show file status
debug:
msg: "Config exists: {{ config_file.stat.exists }}"
- name: Get disk usage
command: df -h /
register: disk_usage
changed_when: false
- name: Parse command output
debug:
var: disk_usage.stdout_lines
- name: Complex registration
shell: |
echo "Database backups:"
ls -lh /backup/db/*.sql 2>/dev/null | wc -l
register: backup_count
failed_when: backup_count.rc != 0
changed_when: false
- name: Use registered variable
debug:
msg: "Found {{ backup_count.stdout_lines[1] }} backups"
Variable Templating with Jinja2
---
- name: Advanced variable usage
hosts: all
vars:
app_config:
name: myapp
port: 8080
debug: false
database:
host: localhost
port: 3306
name: myapp_db
tasks:
- name: Use nested variables
debug:
msg: "DB: {{ app_config.database.host }}:{{ app_config.database.port }}"
- name: Conditional variable
debug:
msg: "{{ 'Debug enabled' if app_config.debug else 'Production mode' }}"
- name: List manipulation
debug:
msg: "{{ ['web1', 'web2', 'web3'] | join(', ') }}"
- name: Dictionary manipulation
debug:
msg: "{{ app_config | to_nice_json }}"
Variable Files
---
# vars/production.yml
env: production
debug: false
database_host: db.prod.example.com
api_endpoint: https://api.prod.example.com
# vars/staging.yml
env: staging
debug: true
database_host: db.staging.example.com
api_endpoint: https://api.staging.example.com
# Playbook using variable files
- name: Environment-specific deployment
hosts: all
vars_files:
- "vars/{{ env }}.yml"
tasks:
- name: Show environment
debug:
msg: "Deploying to {{ env }} environment"
The Jinja2 Template Designer Documentation provides comprehensive information about template syntax and filters available in Ansible.
Best Practices for Ansible Linux Automation
Professional ansible linux automation requires adherence to established patterns that ensure scalability, maintainability, and security. Therefore, following these practices elevates your automation from functional to production-grade.
1. Project Organization
# Recommended directory structure
ansible-project/
βββ ansible.cfg # Ansible configuration
βββ inventory/
β βββ production/
β β βββ hosts.yml
β β βββ group_vars/
β β βββ all.yml
β β βββ webservers.yml
β βββ staging/
β βββ hosts.yml
β βββ group_vars/
β βββ all.yml
βββ roles/
β βββ common/
β βββ webserver/
β βββ database/
βββ playbooks/
β βββ site.yml
β βββ webservers.yml
β βββ databases.yml
βββ group_vars/
β βββ all.yml
βββ host_vars/
βββ files/
βββ templates/
βββ vars/
β βββ production.yml
β βββ staging.yml
βββ library/ # Custom modules
βββ filter_plugins/ # Custom filters
βββ requirements.yml # Role dependencies
2. Use Version Control
# Initialize git repository
git init
# Create .gitignore
cat > .gitignore << 'EOF'
*.retry
*.pyc
__pycache__/
.vault_pass
vault_password
*.swp
.DS_Store
EOF
# Add and commit
git add .
git commit -m "Initial Ansible project structure"
# Create feature branch
git checkout -b feature/new-role
# After testing
git checkout main
git merge feature/new-role
3. Encrypt Sensitive Data with Ansible Vault
# Create encrypted file
ansible-vault create secrets.yml
# Edit encrypted file
ansible-vault edit secrets.yml
# Encrypt existing file
ansible-vault encrypt vars/production.yml
# Decrypt file
ansible-vault decrypt vars/production.yml
# Change vault password
ansible-vault rekey secrets.yml
# View encrypted file
ansible-vault view secrets.yml
# Use password file
echo "my_vault_password" > .vault_pass
chmod 600 .vault_pass
# In ansible.cfg
[defaults]
vault_password_file = .vault_pass
Encrypted Variables Example:
# secrets.yml (encrypted)
---
db_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
66386439653966616265626566393537623931323865613766396236633030
3762633137326632376532636235626538353030653834330a303434
api_key: !vault |
$ANSIBLE_VAULT;1.1;AES256
39653139353535353438656538343365636531636432383437333237636334
3966393636396665620a39336265353764373264343765376334353936386462
4. Use Check Mode and Diff
# Dry run (check mode)
ansible-playbook site.yml --check
# Show differences
ansible-playbook site.yml --check --diff
# Limit to specific hosts
ansible-playbook site.yml --check --limit web1.example.com
# Step through tasks
ansible-playbook site.yml --step
5. Implement Proper Error Handling
---
- name: Robust error handling
hosts: all
tasks:
- name: Attempt risky operation
command: /usr/local/bin/might-fail.sh
register: result
failed_when: false # Don't fail playbook
changed_when: result.rc == 0
- name: Handle failure
debug:
msg: "Operation failed: {{ result.stderr }}"
when: result.rc != 0
- name: Ensure cleanup happens
file:
path: /tmp/cleanup-needed
state: absent
always: # Runs even if previous tasks fail
- name: Critical task with retry
uri:
url: https://api.example.com/health
status_code: 200
register: health_check
until: health_check.status == 200
retries: 5
delay: 10
- name: Graceful degradation
block:
- name: Try primary method
command: method1.sh
rescue:
- name: Fallback to secondary
command: method2.sh
always:
- name: Log attempt
lineinfile:
path: /var/log/deployment.log
line: "{{ ansible_date_time.iso8601 }} - Deployment attempted"
6. Use Tags Effectively
---
- name: Tagged playbook
hosts: all
tasks:
- name: Install packages
apt:
name: nginx
state: present
tags:
- packages
- nginx
- name: Configure Nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
tags:
- configuration
- nginx
- name: Deploy application
copy:
src: app/
dest: /var/www/html/
tags:
- deployment
- application
# Run only specific tags
ansible-playbook site.yml --tags "configuration"
# Run multiple tags
ansible-playbook site.yml --tags "nginx,application"
# Skip tags
ansible-playbook site.yml --skip-tags "deployment"
# List available tags
ansible-playbook site.yml --list-tags
7. Document Your Playbooks
---
# webserver-setup.yml
# Purpose: Deploy and configure Nginx web servers
# Author: DevOps Team
# Last Updated: 2025-10-08
# Dependencies: roles/common, roles/nginx
# Usage: ansible-playbook -i inventory/production webserver-setup.yml
- name: Configure web servers for production
hosts: webservers
become: yes
# Pre-flight checks
pre_tasks:
- name: Verify connectivity
ping:
- name: Check disk space
assert:
that: ansible_facts.mounts | selectattr('mount', 'equalto', '/') | map(attribute='size_available') | first > 5000000000
fail_msg: "Insufficient disk space (need 5GB free)"
roles:
- role: common
tags: ['common']
- role: nginx
tags: ['nginx']
# Post-deployment validation
post_tasks:
- name: Verify web server is responding
uri:
url: http://{{ ansible_default_ipv4.address }}
status_code: 200
register: health_check
failed_when: health_check.status != 200
8. Use Ansible Lint
# Install ansible-lint
pip3 install ansible-lint
# Lint playbook
ansible-lint playbook.yml
# Lint entire project
ansible-lint
# Auto-fix issues (where possible)
ansible-lint --fix playbook.yml
# Custom rules
# .ansible-lint
---
skip_list:
- '306' # Shells that use pipes should set the pipefail option
- '204' # Lines should be no longer than 160 chars
exclude_paths:
- roles/external/
- inventory/
9. Test Your Automation
# tests/test_webserver.yml
---
- name: Test web server deployment
hosts: webservers
gather_facts: yes
tasks:
- name: Check Nginx is installed
command: nginx -v
register: nginx_version
failed_when: nginx_version.rc != 0
changed_when: false
- name: Verify Nginx service is running
service_facts:
- name: Assert Nginx is active
assert:
that:
- ansible_facts.services['nginx.service'].state == 'running'
fail_msg: "Nginx is not running"
- name: Test HTTP response
uri:
url: http://localhost
return_content: yes
register: response
- name: Validate response content
assert:
that:
- response.status == 200
- "'Welcome' in response.content"
fail_msg: "Web server not responding correctly"
The Red Hat Ansible Best Practices documentation provides additional guidance on professional automation development.
Frequently Asked Questions
Is Ansible only for Linux systems?
No, while Ansible excels at Linux automation, it also supports:
- Windows: Using WinRM protocol instead of SSH
- Network Devices: Cisco, Juniper, Arista, etc.
- Cloud Platforms: AWS, Azure, GCP, OpenStack
- Containers: Docker, Kubernetes
- MacOS: For workstation management
# Windows example
- name: Manage Windows servers
hosts: windows_servers
tasks:
- name: Install IIS
win_feature:
name: Web-Server
state: present
How does Ansible compare to Chef or Puppet?
Feature | Ansible | Chef | Puppet |
---|---|---|---|
Agent Required | No | Yes | Yes |
Language | YAML | Ruby DSL | Puppet DSL |
Learning Curve | Low | Moderate | Moderate |
Push/Pull | Push | Pull | Pull |
Best For | Simplicity | Complex workflows | Large enterprises |
Can Ansible manage Docker containers?
Yes, Ansible has extensive Docker support:
---
- name: Manage Docker containers
hosts: docker_hosts
tasks:
- name: Pull Docker image
docker_image:
name: nginx
source: pull
- name: Run container
docker_container:
name: web
image: nginx
state: started
ports:
- "80:80"
volumes:
- /data:/usr/share/nginx/html
How do I handle secrets in Ansible?
Use Ansible Vault for encrypting sensitive data:
# Encrypt variable
ansible-vault encrypt_string 'secret_password' --name 'db_password'
# Result to include in playbook:
db_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
...encrypted content...
# Run with vault password
ansible-playbook site.yml --ask-vault-pass
Can Ansible replace Bash scripts?
Ansible complements rather than replaces Bash:
- Use Ansible for: Configuration management, orchestration, idempotent operations
- Use Bash for: Complex logic, system-specific operations, rapid prototyping
# Ansible can call Bash scripts
- name: Run complex bash script
script: complex-logic.sh
args:
executable: /bin/bash
How do I speed up Ansible playbooks?
# Enable pipelining (ansible.cfg)
[ssh_connection]
pipelining = True # Use strategy plugins – name: Fast execution hosts: all strategy: free # Don’t wait for all hosts tasks: – name: Parallel task command: long-running-command # Increase forks
[defaults]
forks = 20 # Limit fact gathering – name: Skip facts hosts: all gather_facts: no
What’s the difference between ansible and ansible-playbook?
# ansible: Ad-hoc commands (quick tasks)
ansible all -m ping
ansible webservers -m service -a "name=nginx state=restarted"
# ansible-playbook: Run playbooks (complex automation)
ansible-playbook site.yml
Troubleshooting Common Ansible Issues
Issue: SSH Connection Failures
Symptom: “Failed to connect to host via ssh”
# Test SSH manually
ssh -vvv user@host
# Common fixes:
# 1. Add SSH key
ssh-copy-id user@host
# 2. Specify SSH key in inventory
[webservers]
web1 ansible_ssh_private_key_file=~/.ssh/custom_key # 3. Disable host key checking (testing only) export ANSIBLE_HOST_KEY_CHECKING=False # 4. Check SSH agent eval $(ssh-agent) ssh-add ~/.ssh/id_rsa # 5. Use password authentication ansible-playbook site.yml –ask-pass
Issue: Python Not Found on Target
Symptom: “/bin/sh: python: not found”
# Solution 1: Specify Python interpreter
[webservers:vars]
ansible_python_interpreter=/usr/bin/python3 # Solution 2: Auto-discover Python ansible_python_interpreter=auto_silent # Solution 3: Install Python first – name: Install Python raw: apt-get update && apt-get install -y python3 become: yes
Issue: Privilege Escalation Failures
Symptom: “Missing sudo password”
# Prompt for sudo password
ansible-playbook site.yml --ask-become-pass
# Use NOPASSWD in sudoers
# /etc/sudoers.d/ansible
deploy ALL=(ALL) NOPASSWD: ALL
# Test privilege escalation
ansible all -m command -a "id" -b
Issue: Module Not Found
Symptom: “The module X was not found”
# Check module availability
ansible-doc -l | grep module_name
# Install collection
ansible-galaxy collection install community.general
# Verify module location
ansible-config dump | grep DEFAULT_MODULE_PATH
# Use full module name
- name: Use full module path
community.general.snap:
name: hello-world
Issue: Playbook Hanging
Symptom: Task runs indefinitely without completion
# Increase timeout
[defaults]
timeout = 30 # Use async tasks – name: Long running task command: /usr/local/bin/long-task.sh async: 3600 # Maximum runtime poll: 10 # Check every 10 seconds # Debug with verbosity ansible-playbook site.yml -vvv
Issue: Templating Errors
Symptom: “AnsibleUndefinedVariable: ‘variable’ is undefined”
# Use default filter
{{ variable_name | default('default_value') }}
# Check if variable is defined
{% if variable_name is defined %}
{{ variable_name }}
{% endif %}
# Use mandatory filter (fail if undefined)
{{ variable_name | mandatory }}
# Debug variables
- name: Show all variables
debug:
var: hostvars[inventory_hostname]
Issue: Facts Not Updating
Symptom: Stale cached facts
# Clear fact cache
rm -rf /tmp/ansible_facts/*
# Disable fact caching
[defaults]
gathering = smart fact_caching = False # Force fact gathering ansible all -m setup –tree /tmp/facts # Gather subset of facts – name: Gather minimal facts hosts: all gather_facts: yes gather_subset: – ‘!all’ – ‘network’
Issue: Slow Playbook Execution
Symptom: Playbooks taking too long
# Enable profiling
# ansible.cfg
[defaults]
callbacks_enabled = profile_tasks, timer # Increase parallel execution
[defaults]
forks = 20 # Use mitogen (significant speedup) pip install mitogen # ansible.cfg
[defaults]
strategy_plugins = /path/to/mitogen/ansible_mitogen/plugins/strategy strategy = mitogen_linear # Profile playbook execution ANSIBLE_STRATEGY=profile_tasks ansible-playbook site.yml
The Ansible Troubleshooting Guide provides comprehensive debugging strategies and solutions.
Real-World Use Cases
1: Complete LAMP Stack Deployment
---
# lamp-stack.yml
- name: Deploy LAMP Stack
hosts: webservers
become: yes
vars:
mysql_root_password: "{{ vault_mysql_root_password }}"
app_db_name: myapp
app_db_user: myapp_user
app_db_password: "{{ vault_app_db_password }}"
tasks:
- name: Install LAMP packages
apt:
name:
- apache2
- mysql-server
- php
- php-mysql
- python3-pymysql
state: present
update_cache: yes
- name: Start services
service:
name: "{{ item }}"
state: started
enabled: yes
loop:
- apache2
- mysql
- name: Secure MySQL installation
mysql_user:
name: root
password: "{{ mysql_root_password }}"
login_unix_socket: /var/run/mysqld/mysqld.sock
- name: Create application database
mysql_db:
name: "{{ app_db_name }}"
state: present
login_user: root
login_password: "{{ mysql_root_password }}"
- name: Create database user
mysql_user:
name: "{{ app_db_user }}"
password: "{{ app_db_password }}"
priv: "{{ app_db_name }}.*:ALL"
state: present
login_user: root
login_password: "{{ mysql_root_password }}"
- name: Deploy PHP application
copy:
src: webapp/
dest: /var/www/html/
owner: www-data
group: www-data
- name: Configure Apache virtual host
template:
src: vhost.conf.j2
dest: /etc/apache2/sites-available/myapp.conf
notify: Restart Apache
- name: Enable site
command: a2ensite myapp.conf
notify: Restart Apache
handlers:
- name: Restart Apache
service:
name: apache2
state: restarted
2: Multi-Tier Application with Load Balancer
---
# deploy-infrastructure.yml
- name: Configure load balancers
hosts: loadbalancers
become: yes
roles:
- haproxy
vars:
backend_servers: "{{ groups['webservers'] }}"
- name: Deploy web tier
hosts: webservers
become: yes
serial: 2 # Rolling deployment
roles:
- common
- nginx
- application
tasks:
- name: Remove from load balancer
haproxy:
state: disabled
host: "{{ inventory_hostname }}"
backend: webapp
delegate_to: "{{ item }}"
loop: "{{ groups['loadbalancers'] }}"
- name: Deploy new version
include_role:
name: application
- name: Health check
uri:
url: http://localhost/health
status_code: 200
register: health
until: health.status == 200
retries: 10
delay: 3
- name: Add back to load balancer
haproxy:
state: enabled
host: "{{ inventory_hostname }}"
backend: webapp
delegate_to: "{{ item }}"
loop: "{{ groups['loadbalancers'] }}"
- name: Configure database cluster
hosts: databases
become: yes
serial: 1
roles:
- mysql
- mysql-replication
3: Security Hardening
---
# security-hardening.yml
- name: Harden Linux servers
hosts: all
become: yes
tasks:
- name: Update all packages
apt:
upgrade: dist
update_cache: yes
when: ansible_os_family == "Debian"
- name: Disable root login
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^PermitRootLogin'
line: 'PermitRootLogin no'
notify: Restart SSH
- name: Disable password authentication
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^PasswordAuthentication'
line: 'PasswordAuthentication no'
notify: Restart SSH
- name: Configure firewall
ufw:
rule: "{{ item.rule }}"
port: "{{ item.port }}"
proto: "{{ item.proto }}"
loop:
- { rule: 'limit', port: '22', proto: 'tcp' }
- { rule: 'allow', port: '80', proto: 'tcp' }
- { rule: 'allow', port: '443', proto: 'tcp' }
- name: Enable firewall
ufw:
state: enabled
- name: Install fail2ban
apt:
name: fail2ban
state: present
- name: Configure fail2ban
copy:
src: jail.local
dest: /etc/fail2ban/jail.local
notify: Restart fail2ban
handlers:
- name: Restart SSH
service:
name: sshd
state: restarted
- name: Restart fail2ban
service:
name: fail2ban
state: restarted
Additional Resources
Official Documentation
- Ansible Documentation – Complete official documentation
- Ansible Galaxy – Community roles and collections
- Ansible GitHub – Source code and issues
- Red Hat Ansible Automation Platform – Enterprise support
Learning Resources
- Ansible for DevOps Book – Comprehensive guide by Jeff Geerling
- Linux Academy/A Cloud – Ansible courses
- Ansible Community – Forums and chat
Related LinuxTips.pro Guides
- Bash Scripting Basics – Foundation for automation
- System Services with systemd – Service management
- Linux Task Scheduling – Cron and timers
- SSH Server Setup and Security – Secure remote access
Tools and Utilities
- Ansible Lint – Playbook linting
- Molecule – Testing framework
- Ansible Tower/AWX – Web UI and API
- Mitogen for Ansible – Performance boost
Conclusion
Mastering ansible linux automation transforms infrastructure management from manual, error-prone tasks into reliable, repeatable processes. By leveraging Ansible’s agentless architecture, declarative YAML syntax, and extensive module library, you can automate everything from simple package installations to complex multi-tier application deployments.
Furthermore, Ansible’s idempotent nature ensures safe reexecution, making it ideal for maintaining configuration compliance across dynamic infrastructure. The combination of roles for code reusability, variables for flexibility, and Ansible Vault for security creates a complete automation framework suitable for organizations of any size.
Remember to start with simple playbooks, gradually incorporate roles and best practices, and always test in non-production environments before deploying changes. Proper use of version control, documentation, and testing ensures your automation remains maintainable and reliable as your infrastructure grows.
Start implementing ansible linux automation in your environment today, and you’ll immediately experience reduced manual effort, improved consistency, and faster deployment cycles across your entire Linux infrastructure.
Last Updated: October 2025