Skip to main content

Command Palette

Search for a command to run...

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

Published
6 min read
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.udemy.com/course/devops-for-beginners-docker-k8s-cloud-cicd-4-projects/learn/lecture/49899673#overview

https://www.youtube.com/watch?v=qJD5UCdtjg4&list=PLVOdqXbCs7bX88JeUZmK4fKTq2hJ5VS89

https://www.linkedin.com/in/pravin-mishra-aws-trainer/

https://github.com/omiete01