Terraform creates infrastructure. Ansible configures it. After Terraform provisions your servers, someone has to install software on them, configure services, manage users, and keep everything consistent as the infrastructure scales. Doing this manually does not scale. Ansible automates server configuration using simple YAML files called playbooks, and it does this without requiring any agent software on the target servers — it works over SSH.
Ansible's main advantage is simplicity. It uses YAML, which reads almost like English. It is agentless — no software needs to be installed on the servers you manage. It is idempotent — running the same playbook twice produces the same result without causing unintended changes. For DevOps teams, this means configuration is self-documenting, version-controllable, and repeatable.
# Ubuntu/Debian
sudo apt update
sudo apt install ansible
# Or via pip
pip install ansible
# Verify installation
ansible --version
An inventory file lists the servers Ansible will manage. You can group servers logically by function, environment, or region.
# inventory.ini
[webservers]
web1.srjahir.in
web2.srjahir.in
192.168.1.10
[databases]
db1.srjahir.in ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa
[staging]
staging.srjahir.in
[production:children]
webservers
databases
# Ping all servers
ansible all -i inventory.ini -m ping
# Run a command on all webservers
ansible webservers -i inventory.ini -m command -a "uptime"
# Install a package on all servers
ansible all -i inventory.ini -m apt -a "name=nginx state=present" --become
# Copy a file to servers
ansible webservers -i inventory.ini -m copy -a "src=./config.conf dest=/etc/nginx/nginx.conf" --become
# Restart a service
ansible webservers -i inventory.ini -m service -a "name=nginx state=restarted" --become
---
- name: Configure Web Servers
hosts: webservers
become: yes # run as sudo
vars:
nginx_port: 80
app_user: webuser
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install required packages
apt:
name:
- nginx
- python3
- python3-pip
state: present
- name: Create application user
user:
name: "{{ app_user }}"
shell: /bin/bash
create_home: yes
- name: Copy Nginx configuration
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/myapp
notify: Reload Nginx
- name: Enable site
file:
src: /etc/nginx/sites-available/myapp
dest: /etc/nginx/sites-enabled/myapp
state: link
- name: Ensure Nginx is running
service:
name: nginx
state: started
enabled: yes
handlers:
- name: Reload Nginx
service:
name: nginx
state: reloaded
# Run a playbook
ansible-playbook -i inventory.ini deploy-webserver.yml
# Dry run (check mode - no actual changes)
ansible-playbook -i inventory.ini deploy-webserver.yml --check
# Run with verbose output
ansible-playbook -i inventory.ini deploy-webserver.yml -v
# Run only specific tasks using tags
ansible-playbook -i inventory.ini deploy-webserver.yml --tags "install"
As playbooks grow, organizing tasks into roles keeps things manageable. A role is a standardized file structure for grouping related tasks, templates, and variables.
roles/
nginx/
tasks/
main.yml # Task list
templates/
nginx.conf.j2 # Jinja2 template
vars/
main.yml # Variables
defaults/
main.yml # Default variables
handlers/
main.yml # Handlers
In Part 9, we will cover monitoring and observability — how DevOps teams use Prometheus, Grafana, and logging tools to know what is happening inside their systems at all times.
Ansible's inventory defines the hosts and groups that playbooks target. Dynamic inventory is essential for cloud environments where servers are created and destroyed frequently — cloud provider plugins automatically generate inventory from your actual running instances rather than maintaining a static list. Variables in Ansible follow a precedence hierarchy: host variables override group variables, which override inventory variables, which override role defaults. Understanding this hierarchy prevents unexpected behavior when the same variable is defined in multiple places. Use ansible-vault to encrypt sensitive variables — passwords, API keys — rather than storing them in plaintext in your repository.
---
- name: Configure web server
hosts: webservers
become: yes
vars:
nginx_port: 80
tasks:
- name: Install nginx
apt:
name: nginx
state: present
update_cache: yes
- name: Start and enable nginx
systemd:
name: nginx
state: started
enabled: yes
- name: Deploy configuration
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Reload nginx
handlers:
- name: Reload nginx
systemd:
name: nginx
state: reloaded
Write an Ansible playbook that configures a web server: install nginx, deploy a simple HTML page using a template, configure a systemd service, and verify the service is running. Run it against a local Docker container or a cloud VM. Then modify the playbook to be idempotent — verify that running it twice produces no changes on the second run. Idempotency is a fundamental Ansible principle: running a playbook should bring the system to the desired state regardless of its starting state.
DevOps is not a destination but a continuous journey of improvement. The practices covered here — automation, monitoring, infrastructure as code, CI/CD pipelines — are tools in service of a deeper goal: enabling teams to deliver software changes to production quickly, safely, and reliably. The measurement that matters is not which tools you use but how long it takes to go from a committed code change to running in production, and how confident you are in that process. The best DevOps teams measure their deployment frequency, lead time for changes, change failure rate, and mean time to recovery (the DORA metrics), and treat these as engineering objectives to improve over time.
Every topic in technology and finance rewards the learner who goes beyond surface understanding to build genuine fluency. Fluency comes from repeated exposure, application in varied contexts, and reflection on what worked and what did not. The concepts discussed here are starting points — each one opens into a deeper field of study that could occupy years of focused learning. The most effective approach is not to try to master everything at once, but to pick the areas most relevant to your current goals, go deep there, and then expand. Depth in a few areas is more valuable than shallow familiarity with many. Build on what you know, stay curious about what you do not, and keep the practice of learning as a consistent daily habit rather than an occasional burst of effort.
The questions that make learning stick: How does this connect to what I already know? Where would I actually use this? What would happen if I tried to explain this to someone who knows nothing about it? What are the edge cases and exceptions? What is still unclear? Asking these questions transforms passive reading into active learning, and active learning is what builds the kind of understanding that is still accessible years later when you need it under real conditions.