Ansible Guide — Automation and Configuration Management
Ansible is an open-source automation tool that lets you manage servers, deploy applications, and configure infrastructure using simple YAML playbooks — with no agent software required on managed nodes, just SSH and Python.
What You’ll Learn
- Setting up Ansible with inventory files and SSH connectivity
- Writing playbooks with tasks, handlers, and variables
- Using Ansible modules for system administration
- Organizing automation with roles and directory structures
- Understanding idempotency and running ad-hoc commands
Why Ansible Matters
Configuration management tools like Puppet and Chef require agents installed on every node, a master server, and a complex domain-specific language. Ansible uses SSH (no agents), YAML (no new language to learn), and push-based execution — you run a playbook and it takes effect immediately. This makes Ansible ideal for teams that need automation without the overhead of a full configuration management platform. DodaTech uses Ansible to provision Durga Antivirus Pro’s update servers — the playbooks ensure every server has the correct Nginx config, SSL certificates, and update repositories with a single command.
flowchart LR
A[Bash & Linux Basics] --> B[Ansible]
B --> C[Inventory]
B --> D[Playbooks]
B --> E[Modules]
B --> F[Roles]
C --> G[Server Groups]
D --> H[Task Automation]
E --> I[System Operations]
F --> J[Reusable Automation]
style B fill:#e0002b,color:#fff
Core Concepts
Inventory and Ad-Hoc Commands
# Install Ansible
# Ubuntu/Debian: sudo apt install ansible
# macOS: brew install ansible
# RHEL/CentOS: sudo yum install epel-release && sudo yum install ansible
# Inventory file — define your servers
# hosts.ini
[webservers]
web1.example.com
web2.example.com
[databases]
db1.example.com ansible_user=ubuntu
[all:vars]
ansible_user=deploy
ansible_ssh_private_key_file=~/.ssh/prod_key# Ad-hoc commands — run commands without a playbook
ansible all -i hosts.ini -m ping
# Output:
# web1.example.com | SUCCESS => {
# "changed": false,
# "ping": "pong"
# }
# web2.example.com | SUCCESS => { "ping": "pong" }
# db1.example.com | SUCCESS => { "ping": "pong" }
# Check system uptime
ansible webservers -i hosts.ini -m shell -a "uptime"
# Copy a file to all servers
ansible all -i hosts.ini -m copy -a "src=/etc/hosts dest=/tmp/hosts"Playbooks
Playbooks are YAML files that define automation workflows:
---
- name: Configure web server
hosts: webservers
become: yes
vars:
nginx_port: 8080
app_version: "1.2.3"
tasks:
- name: Install Nginx
apt:
name: nginx
state: present
notify: Restart Nginx
- name: Create app directory
file:
path: /var/www/myapp
state: directory
owner: www-data
group: www-data
mode: "0755"
- name: Deploy application config
template:
src: nginx.conf.j2
dest: /etc/nginx/sites-available/myapp
notify: Restart Nginx
- name: Enable site
file:
src: /etc/nginx/sites-available/myapp
dest: /etc/nginx/sites-enabled/myapp
state: link
notify: Restart Nginx
- name: Check if app is responding
uri:
url: "http://localhost:{{ nginx_port }}/health"
status_code: 200
handlers:
- name: Restart Nginx
service:
name: nginx
state: restarted# Run the playbook
ansible-playbook -i hosts.ini webserver.yml
# Output:
# PLAY [Configure web server] ************************************************
# TASK [Install Nginx] *******************************************************
# changed: [web1.example.com]
# TASK [Create app directory] ************************************************
# ok: [web1.example.com]
# ...
# PLAY RECAP *****************************************************************
# web1.example.com : ok=5 changed=3 unreachable=0 failed=0Output: Each task reports ok (no change needed) or changed (Ansible made a change). This is idempotency — running the playbook again produces no changes unless the server state drifts from the desired state.
Variables and Templates
# group_vars/webservers.yml — variables per server group
---
nginx_port: 8080
app_domain: "app.example.com"
ssl_cert_path: "/etc/ssl/certs/{{ app_domain }}.pem"{# templates/nginx.conf.j2 — Jinja2 template #}
server {
listen {{ nginx_port }};
server_name {{ app_domain }};
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
}
location /health {
return 200 "healthy\n";
}
}Roles — Reusable Automation
Roles organize playbooks into reusable components:
# Create a role structure
ansible-galaxy init nginx
# Output:
# nginx/
# ├── tasks/
# │ └── main.yml
# ├── handlers/
# │ └── main.yml
# ├── templates/
# ├── files/
# ├── vars/
# │ └── main.yml
# ├── defaults/
# │ └── main.yml
# └── meta/
# └── main.yml# nginx/tasks/main.yml
---
- name: Install Nginx
apt:
name: nginx
state: present
- name: Configure Nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Restart Nginx
- name: Start Nginx
service:
name: nginx
state: started
enabled: yes# playbook.yml — using the role
---
- hosts: webservers
become: yes
roles:
- role: nginx
vars:
nginx_port: 8080
- role: app_deployCommon Mistakes
Not using
become: yesfor system-level tasks: Installing packages, editing system configs, and restarting services require root. Forgettingbecome: yescauses permission denied errors.Writing non-idempotent tasks: A task that appends to a file without checking if the line already exists will add duplicates on every run. Use
lineinfileorblockinfilemodules instead ofshell.Storing secrets in plaintext variables: Never put passwords or API keys in playbooks. Use Ansible Vault (
ansible-vault encrypt) or external secret managers.Not using handlers for service restarts: Calling
service: state=restartedin a task restarts the service every run. Handlers only restart when a task reportschanged.Running playbooks against production without
--check: The--checkflag does a dry run. Always runansible-playbook --check --diffto see what changes will be made before executing.
Practice Questions
What does it mean for Ansible to be “agentless”? Answer: Ansible doesn’t require software installed on managed nodes. It connects via SSH and runs Python scripts that are removed after execution.
How do handlers differ from regular tasks? Answer: Handlers run only when notified by a task that reports
changed. They run once, at the end of the play, even if multiple tasks notify them.What is the purpose of an inventory file? Answer: The inventory defines which servers Ansible manages, groups them logically (webservers, databases), and specifies connection variables (user, key, port).
How does Ansible ensure idempotency? Answer: Each module checks the current state before making changes. If the desired state matches the current state, no change is made. Running the same playbook twice is safe.
Challenge
Automate a LAMP stack deployment: create an inventory with one server, write a playbook that installs Apache, MySQL, and PHP, configure virtual hosts with a Jinja2 template, deploy a PHP application from Git, set up a firewall with ufw module, use Ansible Vault to store the MySQL root password, and test idempotency by running the playbook twice.
FAQ
Try It Yourself
# Install Ansible locally
sudo apt install ansible -y
# Create a local inventory
echo -e "localhost ansible_connection=local" > hosts.ini
# Run a ping test
ansible all -i hosts.ini -m ping
# Run a playbook that creates a file
ansible localhost -m file -a "path=/tmp/ansible-test state=touch"
ls -la /tmp/ansible-testWhat’s Next
| Topic | Description |
|---|---|
| Infrastructure provisioning alongside Ansible | |
| Container orchestration with automation |
Related topics: Bash, Linux, AWS, Docker
What’s Next
Congratulations on completing this Ansible tutorial! Here’s where to go from here:
- Practice daily — Consistency is more important than long study sessions
- Build a project — Apply what you learned by building something real
- Explore related topics — Check out other tutorials in the same category
- Join the community — Discuss with other learners and share your progress
Remember: every expert was once a beginner. Keep coding!
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro