---
# Main playbook
- name: Configure web servers
hosts: webservers
become: yes
vars:
app_name: web-app
app_user: deploy
app_dir: /opt/web-app
node_version: "20"
nginx_worker_processes: auto
pre_tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
roles:
- common
- nodejs
- nginx
- app
handlers:
- name: restart nginx
service:
name: nginx
state: restarted
- name: restart app
systemd:
name: "{{ app_name }}"
state: restarted
daemon_reload: yes
tasks:
# System packages
- name: Install system packages
apt:
name:
- build-essential
- git
- curl
- htop
- unzip
state: present
tags: [packages]
# Create app user
- name: Create application user
user:
name: "{{ app_user }}"
system: yes
shell: /bin/bash
home: "{{ app_dir }}"
createhome: yes
# App directory
- name: Create app directories
file:
path: "{{ item }}"
state: directory
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: "0755"
loop:
- "{{ app_dir }}"
- "{{ app_dir }}/releases"
- "{{ app_dir }}/shared"
- "{{ app_dir }}/shared/logs"
- /var/log/{{ app_name }}
# Deploy app
- name: Clone application repository
git:
repo: "https://github.com/example/{{ app_name }}.git"
dest: "{{ app_dir }}/releases/{{ app_version }}"
version: "{{ app_version }}"
become_user: "{{ app_user }}"
tags: [deploy]
# Install dependencies
- name: Install npm dependencies
npm:
path: "{{ app_dir }}/releases/{{ app_version }}"
production: yes
become_user: "{{ app_user }}"
tags: [deploy]
# Symlink current release
- name: Symlink current release
file:
src: "{{ app_dir }}/releases/{{ app_version }}"
dest: "{{ app_dir }}/current"
state: link
owner: "{{ app_user }}"
notify: restart app
tags: [deploy]
# App environment file
- name: Create environment file
template:
src: templates/env.j2
dest: "{{ app_dir }}/shared/.env"
owner: "{{ app_user }}"
mode: "0600"
notify: restart app
tags: [config]
# Systemd service
- name: Create systemd service
template:
src: templates/app.service.j2
dest: /etc/systemd/system/{{ app_name }}.service
mode: "0644"
notify: restart app
tags: [service]
# Nginx config
- name: Configure nginx site
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/{{ app_name }}
mode: "0644"
notify: restart nginx
tags: [nginx]
- name: Enable nginx site
file:
src: /etc/nginx/sites-available/{{ app_name }}
dest: /etc/nginx/sites-enabled/{{ app_name }}
state: link
notify: restart nginx
tags: [nginx]
# Enable and start services
- name: Enable and start app
systemd:
name: "{{ app_name }}"
enabled: yes
state: started
daemon_reload: yes
tags: [service]
# Firewall
- name: Configure firewall
ufw:
rule: allow
port: "{{ item }}"
proto: tcp
loop:
- "22"
- "80"
- "443"
tags: [security]
- name: Enable firewall
ufw:
state: enabled
policy: deny
tags: [security]
# Ansible inventory
[webservers]
web1.example.com ansible_host=10.0.1.10
web2.example.com ansible_host=10.0.1.11
web3.example.com ansible_host=10.0.1.12
[dbservers]
db1.example.com ansible_host=10.0.2.10
db2.example.com ansible_host=10.0.2.11
[workers]
worker1.example.com ansible_host=10.0.3.10
worker2.example.com ansible_host=10.0.3.11
[production:children]
webservers
dbservers
workers
[production:vars]
ansible_user=deploy
ansible_ssh_private_key_file=~/.ssh/deploy_key
app_version=v1.2.0
environment=production
Ansible automates server configuration and application deployment without agents. Playbooks are YAML files describing desired system state. hosts targets machines from the inventory. tasks execute modules like apt, copy, template, service, and user. handlers restart services only when notified of changes. roles organize related tasks, templates, and variables into reusable packages. Variables can be defined in group_vars, host_vars, or passed at runtime. when conditionals skip tasks based on facts or variables. with_items and loop iterate over lists. Jinja2 templates generate configuration files dynamically. become: yes escalates privileges with sudo. tags enable selective task execution. Idempotent tasks safely run repeatedly.