Ansible Playbooks for Configuration Management â Complete Automation Guide
In this tutorial, you'll learn about Ansible Playbooks for Configuration Management. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
Ansible playbooks are YAML-based automation scripts that define desired server states through tasks, roles, and inventory management, enabling Configuration Management, application deployment, and Orchestration across thousands of servers without requiring agent software.
What You'll Learn
Why It Matters
Manually configuring servers is error-prone and does not scale beyond a handful of machines. When you need to update the NGINX configuration on 200 web servers, apply a security patch to all database hosts, or onboard a new developer workstation, doing it manually guarantees mistakes. Ansible playbooks execute these tasks consistently, idempotently, and with full auditability -- every run is logged, every change is visible.
Real-World Use
DodaTech uses Ansible to manage 500+ servers across development, staging, and production environments. A single <a href="/devops/ansible/">ansible</a>-playbook site.yml --limit production command applies OS patches, updates Durga Antivirus Pro scan agents, rotates TLS certificates, and verifies compliance -- all in under 15 minutes with zero manual intervention.
flowchart TD
A["Control Node"] --> B["Inventory: production"]
A --> C["Playbook: site.yml"]
B --> D["Web Servers (50 hosts)"]
B --> E["API Servers (30 hosts)"]
B --> F["Database Servers (10 hosts)"]
C --> G["Play 1: All hosts"]
C --> H["Play 2: Web servers"]
C --> I["Play 3: Database servers"]
G --> J["System patching"]
G --> K["Users & SSH keys"]
G --> L["Monitoring agents"]
H --> M["Deploy NGINX config"]
H --> N["Deploy SSL certs"]
I --> O["Deploy PostgreSQL config"]
style A fill:#EE0000,color:#fff
style C fill:#EE0000,color:#fff
Prerequisites: Linux administration basics, SSH access to target servers, and Ansible installed on the control node (pip install <a href="/devops/ansible/">ansible</a> or apt install <a href="/devops/ansible/">ansible</a>).
Inventory Management
Inventory defines the servers Ansible manages. It can be static (INI/YAML files) or dynamic (cloud API queries).
# inventory/production.ini
[web]
web-01.example.com ansible_host=10.0.1.10
web-02.example.com ansible_host=10.0.1.11
web-03.example.com ansible_host=10.0.1.12
[api]
api-01.example.com ansible_host=10.0.2.10
api-02.example.com ansible_host=10.0.2.11
[database]
db-primary.example.com ansible_host=10.0.3.10
db-replica-01.example.com ansible_host=10.0.3.11
[production:children]
web
api
database
[production:vars]
ansible_user=deploy
ansible_ssh_private_key_file=/home/ansible/.ssh/production.pem
Expected behavior: Ansible connects to each host using SSH as the deploy user with the specified private key. The [production:children] group aggregates all sub-groups, allowing targeting with <a href="/devops/ansible/">ansible</a> -i inventory/production.ini production.
Core Playbook Structure
# site.yml
---
- name: Apply base configuration to all servers
hosts: all
become: yes
vars:
ntp_servers:
- 0.pool.ntp.org
- 1.pool.ntp.org
ssh_port: 22
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
when: ansible_os_family == "Debian"
- name: Install essential packages
apt:
name:
- htop
- curl
- wget
- git
- ufw
- fail2ban
state: present
- name: Configure NTP
template:
src: templates/ntp.conf.j2
dest: /etc/ntp.conf
notify: restart ntp
handlers:
- name: restart ntp
service:
name: ntp
state: restarted
Expected behavior: The playbook runs on all inventory hosts. It updates the apt cache (only on Debian/Ubuntu systems), installs essential packages idempotently, and configures NTP using a Jinja2 template. If the NTP configuration changes, the notify triggers the handler to restart the NTP service.
# templates/ntp.conf.j2
# Managed by Ansible -- do not edit manually
driftfile /var/lib/ntp/ntp.drift
statistics loopstats peerstats clockstats
filegen loopstats file loopstats type day enable
filegen peerstats file peerstats type day enable
filegen clockstats file clockstats type day enable
{% for server in ntp_servers %}
server {{ server }} iburst
{% endfor %}
restrict -4 default kod notrap nomodify nopeer noquery
restrict -6 default kod notrap nomodify nopeer noquery
restrict 127.0.0.1
restrict ::1
Roles for Reusability
Roles organize playbooks into reusable components with a standard directory structure.
# roles/nginx/tasks/main.yml
---
- name: Add Nginx repository
apt_repository:
repo: "deb http://nginx.org/packages/{{ ansible_distribution | lower }}/ {{ ansible_distribution_release }} nginx"
state: present
when: ansible_os_family == "Debian"
- name: Install Nginx
apt:
name: nginx
state: present
- name: Deploy configuration
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: reload nginx
- name: Deploy virtual host
template:
src: vhost.conf.j2
dest: /etc/nginx/conf.d/{{ app_name }}.conf
notify: reload nginx
- name: Ensure Nginx is running
service:
name: nginx
state: started
enabled: yes
# site.yml using roles
---
- name: Configure web servers
hosts: web
become: yes
vars:
app_name: durga-api
domain: api.dodatech.com
ssl_cert_path: /etc/ssl/certs/dodatech.pem
roles:
- common
- nginx
- nodejs
- deploy
- name: Configure database servers
hosts: database
become: yes
vars:
postgres_version: 16
postgres_max_connections: 200
roles:
- common
- <a href="/databases/postgresql/">PostgreSQL</a>
Expected behavior: The web group servers receive the common, nginx, nodejs, and deploy roles in sequence. Each role has its own tasks, handlers, templates, and default variables. The database group servers get common and <a href="/databases/postgresql/">PostgreSQL</a>. Roles are reusable across different playbooks and projects.
Ansible Vault for Secrets
Encrypt sensitive data like passwords and API keys using Ansible Vault.
# group_vars/production/vault.yml
---
vault_db_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
66386439653236336...<encrypted content>
vault_api_key: !vault |
$ANSIBLE_VAULT;1.1;AES256
386239343361363...<encrypted content>
# Create an encrypted variable file
ansible-vault create group_vars/production/vault.yml
# Edit an existing vault file
ansible-vault edit group_vars/production/vault.yml
# Run playbook with vault password file
ansible-playbook site.yml --vault-password-file .vault_pass
Common Errors
Playbook fails with "SSH connection timeout": The control node cannot reach the target host on port 22. Check security group rules, network ACLs, and that the target host is running. Use
<a href="/devops/ansible/">ansible</a> hostname -m ping -i inventory.inito test connectivity first.Idempotency issues with shell/command modules: Using
commandorshellmodules breaks idempotency because Ansible cannot detect whether the command has already been applied. Usecreatesorwhenconditions to make command-based tasks idempotent, or prefer dedicated modules (apt,copy,template,systemd).Variable precedence confusion: Ansible has 22 levels of variable precedence. A variable defined in
group_vars/allis overridden byhost_vars/hostname, which is overridden by--extra-vars. Unexpected variable values are often due to forgetting this hierarchy. Use<a href="/devops/ansible/">ansible</a>-inventory --varsto debug.Becoming root without passwordless sudo: If the remote user requires a password for sudo, add
<a href="/devops/ansible/">Ansible</a>_become_passwordto the inventory or use--ask-become-pass. Without this, tasks that requirebecome: yesfail with "Missing sudo password".Handlers not running when expected: Handlers run at the end of the play, not immediately after the task that notifies them. If a later task depends on the handler action (e.g., a configuration change followed immediately by a service start), use
meta: flush_handlersto force handler execution at a specific point.
Practice Questions
What is idempotency in Ansible and why is it important? Answer: Idempotency means running the same playbook multiple times produces the same result without side effects. Ansible modules are designed to be idempotent -- they check the current state before applying changes. This allows safe re-runs and ensures the system converges to the desired state regardless of the starting point.
How do Ansible roles differ from a flat tasks structure? Answer: Roles encapsulate related tasks, handlers, templates, variables, and defaults into a reusable unit with a standard directory layout. Flat tasks work for simple playbooks but become unmanageable as complexity grows. Roles enable sharing across teams and projects.
What is the purpose of
gather_facts: yesin a play? Answer: Fact gathering collects system information (OS, IP addresses, memory, disk, CPU) from target hosts into the<a href="/devops/ansible/">Ansible</a>_factsvariable. These facts are used for conditional task execution (when: ansible_os_family == "Debian"), template variables, and system-aware automation.How does the
delegate_todirective change task execution? Answer: By default, tasks run on the target host.delegate_toruns a task on a different host (usually the control node or a management server). This is useful for adding a host to a load balancer pool before deploying, or registering DNS records on the DNS server from the web server's play.
Challenge
Create an Ansible playbook that bootstraps a complete web application stack on a fresh Ubuntu 22.04 server: install and configure PostgreSQL 16 with a database and user for the application, install and configure Nginx as a reverse proxy, deploy a Node.js application from a Git repository, configure systemd for the Node.js app, set up UFW firewall rules allowing only SSH, HTTP, and HTTPS, install and configure fail2ban, harden SSH by disabling root login and password authentication, and set up SSL with Let's Encrypt using certbot.
Mini Project
Build a complete Ansible automation suite for a multi-tier environment: create inventory files for dev, staging, and production with appropriate group variables, write roles for common (users, SSH keys, packages, firewall, NTP), nginx (install, configure vhosts, SSL), <a href="/databases/postgresql/">PostgreSQL</a> (install, configure, users, databases, Replication for production), nodejs (install via nvm, deploy app, configure systemd), and monitoring (install and configure Prometheus node exporter and Filebeat). Create a main playbook (site.yml) that applies roles based on group membership, encrypt all secrets with Ansible Vault, use Jinja2 templates for all configuration files, include a comprehensive README.md with usage instructions, and test the entire suite by provisioning a fresh server from scratch.
Related Resources
| Resource | Description |
|---|---|
| Terraform IaC | Provisioning with Terraform |
| CI/CD Pipelines | Automating Ansible runs |
| Linux Administration | Server management basics |
| Git Workflows | Version control for playbooks |
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro