Automation with Ansible Playbook: Building a Modular Web Server Deployment with Multiple Plays

In today’s fast-paced DevOps landscape, consistency, speed, and reliability are non-negotiable. Manual server configuration is error-prone, time-consuming, and impossible to scale. This is where Ansible shines — an open-source automation tool that simplifies infrastructure management through simple, human-readable YAML playbooks. No agents required. No complex setup. Just secure SSH connections and declarative automation.
This article walks you through how to structure complex deployments using multiple, logically separated plays — a hallmark of professional-grade automation.
Why Ansible? The Power of Infrastructure as Code
Ansible enables Infrastructure as Code (IaC), treating your server configurations as version-controlled code. This brings immense benefits:
Consistency: Every server is configured identically, eliminating “works on my machine” problems.
Reproducibility: Spin up new environments in minutes, not days.
Auditability: Every change is tracked in Git, providing a clear history of infrastructure evolution.
Idempotency: Running a playbook multiple times yields the same result — no unintended side effects.
Agentless Architecture: Leverages existing SSH, reducing attack surface and deployment complexity.
Prerequisites:
A control node with Ansible installed (Linux, macOS, or WSL local machine, or Linux VM)
Have SSH access to a managed Ubuntu VM(s)
Your managed VM has:
Public IP address
Port 22 and 80 are open
This article will cover the installation of nginx on a remote Ubuntu server using both single-play and multi-play playbooks. With a multi-play playbook, the tasks will be broken into distinctive, focused plays simulating real-world deployments.
Create a Single-Play Playbook
Step 1:
Create a new directory on your local machine (your control node, where Ansible is installed)
mkdir nginx-setup
cd nginx-setup
Step 2: Create your inventory file
Let’s create the inventory file, which will include the IP address(es) of your Ubuntu VM
nano inventory.ini
This will define the servers Ansible will connect to.
Paste the following (replace the IP with your VM’s public IP):
[web]
<VM_PUBLIC_IP1> # eg 20.185.245.255
<VM_PUBLIC_IP2> # if available
[all:vars]
ansible_user=azureuser # the username of your vm if not azureuser
ansible_ssh_private_key_file=~/.ssh/id_rsa
Explanation:
[web]: a group of web servers (we’re targeting this in our playbook)
ansible_user: your VM’s SSH username (use azureuser for Azure)
ansible_ssh_private_key_file: the path to your SSH private key
Step 3: Test SSH Connection with Ansible
Before writing the playbook, make sure Ansible can talk to your VM.
Run on the command line, in the folder where inventory.ini is located:
ansible all -i inventory.ini -m ping
Expected output:

If this fails:
Check your IP and username
Ensure port 22 is open in your VM’s network security group
Make sure your private key path is correct
Step 4: Create Your First Paybook File
Write a playbook that installs nginx. Create a file named install-nginx.yml
nano install-nginx.yml
Paste the following YAML:
- name: Install nginx on Ubuntu server
hosts: web
become: yes
remote_user: azureuser
tasks:
- name: Ensure nginx is installed
apt:
name: nginx
state: present
- name: Start nginx service
service:
name: nginx
state: started
What this playbook does:
Connects to the web group
Escalates privileges with become: yes to run apt
Installs nginx using the apt module
Ensures the package is only installed if not already present (idempotent)
Step 5: Run the Playbook
Use the ansible-playbook command to execute your playbook:
ansible-playbook -i inventory.ini install-nginx.yml
You should see the below:
PLAY [Install nginx on Ubuntu server] *************************
TASK [Gathering Facts] ****************************************
ok: [<your-vm-ip>]
TASK [Ensure nginx is installed] ******************************
changed: [<your-vm-ip>]
If the task shows changedAnsible installed nginx. If it says ok, nginx was already installed.
Step 6: Test Idempotence
Re-run the same command:
ansible-playbook -i inventory.ini install-nginx.yml
This time, you should see:
ok: []
That means Ansible didn’t repeat the installation — because the desired state was already achieved. This is called idempotence, and it’s a key principle in automation.
Step 7: Verify Nginx is Running
Option 1: Use Curl from your control node:
curl http://<vm_public_ip>
You should see HTML output from the Nginx default page.

Option 2: Open in your browser
Go to http://<vm_public_ip>
You should see the Nginx welcome page. If not:
Check that port 80 is open in the VM’s firewall settings
Restart nginx manually with:
sudo systemctl restart nginx
Recap
With this single play, you:
installed Nginx on a remote server using Ansible
verified automation using browser and curl
learned about idempotence and modularity
Create a Multi-Play Playbook
Step 1: Create a Custom HomePage
Create a simple static HTML file to deploy
First, create a directory: mkdir files
Create the HTML file: nano files/index.html
Copy and paste the following into the file:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to Ansible Web Server</title>
</head>
<body>
<h1>Success! This page was deployed with Ansible.</h1>
</body>
</html>
This file will replace the default nginx welcome page
Step 2: Write the Multi-Play Playbook
Create a new playbook named webserver-deploy.yml: nano webserver-deploy.yml
Paste in the following YAML:
- name: Install nginx on web servers
hosts: web
become: yes
remote_user: azureuser
tasks:
- name: Ensure nginx is installed
apt:
name: nginx
state: present
update_cache: yes
- name: Deploy static website content
hosts: web
become: yes
remote_user: azureuser
tasks:
- name: Copy custom index.html
copy:
src: files/index.html
dest: /var/www/html/index.html
owner: www-data
group: www-data
mode: '0644'
- name: Restart nginx to apply changes
service:
name: nginx
state: restarted
What this playbook does:
Play 1: Installs nginx (same as before)
Play 2: Copies a static HTML page and restarts nginx
The copy module is used to push content
The service module ensures the new page is served
This playbook includes multiple plays, a clear purpose, and clean logic
Step 3: Run the Playbook
Run the playbook with this command: ansible-playbook -i inventory.ini webserver-deploy.yml
You should see something like the below:

I have 2 VMs in my inventory.ini file, hence the result
If something fails, carefully check indentation — YAML is space-sensitive.
Step 4: Verify Deployment
Option 1: Use Curl on the command line: curl <vm_ip>
You should see the raw HTML output
Option 2: Use Browser: http://<vm_ip>
You should see your custom page with the Success message.
Step 5: Make a Change and Redeploy
Change the content of files/index.html
Update the <h1> line to something new (e.g., add your name or timestamp).
Re-run the playbook after saving changes: ansible-playbook -i inventory.ini webserver-deploy.yml
Test deployment again and reload your browser to see changes.

This helps reinforce:
The power of reusability
How Ansible copies files only when they change
How automation and deployment go hand-in-hand
Recap:
With this multi-play, you:
separated logic between installation and deployment
used the copy and service modules
managed supporting files cleanly using a files/ directory
verified the results using a browser and curl
practiced real-world deployment using Ansible
Conclusion: Automate Like a Pro
This isn’t just about installing Nginx — it’s about thinking in automation patterns. By mastering multi-play playbooks, you move from scripting to orchestration. You begin to design systems that are:
Modular → Easier to test and reuse
Maintainable → Clear separation of duties
Resilient → Idempotent, reliable, and scalable
Whether you’re managing Windows/Linux systems, Kubernetes clusters, or high-availability web services, Ansible gives you the control, clarity, and confidence to automate with excellence.
P.S. This post is part of the FREE DevOps Micro Internship (DMI) Cohort run by Pravin Mishra.
You can start your DevOps journey for free from his YouTube Playlist.
Resources
https://docs.ansible.com/projects/ansible/latest/collections/index_module.html
https://www.youtube.com/watch?v=qJD5UCdtjg4&list=PLVOdqXbCs7bX88JeUZmK4fKTq2hJ5VS89
