Ansible — Variables, Handlers and Jinja2 Templates
Module 08 continues. The skeleton from last session (roles for Nginx, MariaDB, PHP-FPM) worked — but every value was hardcoded. Today: variables to centralise config, handlers to restart services only when needed, and Jinja2 templates to generate config files dynamically.
Repo: biroue10/ansible-lemp-stack
The problem with hardcoded values
1
2
3
4
- name: Install Nginx
dnf:
name: nginx # hardcoded
state: present
If you manage 50 servers with different configs, hardcoded tasks become unmaintainable. One value changes → you edit it in 50 places → you introduce errors.
Variables — defaults/main.yml
Each role has a defaults/ directory for default variable values:
1
2
3
4
# roles/nginx/defaults/main.yml
nginx_package: nginx
nginx_service: nginx
nginx_worker_processes: auto
Reference them in tasks with Jinja2 syntax. When a value starts with `” state: present
- name: Start nginx service: name: “” state: started enabled: true ```
Rule: Never use hyphens in Ansible variable names — use underscores.
1
2
php_fpm_service: php-fpm # ✅ variable name uses underscore, value can have hyphen
php-fpm_service: php-fpm # ❌ causes a parsing error
Handlers — restart only when config changes
Restarting Nginx on every playbook run drops active connections unnecessarily. A handler only runs when explicitly notified — and is only notified when the triggering task reports changed.
1
2
3
4
5
# roles/nginx/handlers/main.yml
- name: restart nginx
service:
name: ""
state: restarted
1
2
3
4
5
6
# roles/nginx/tasks/main.yml
- name: Deploy nginx config
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: restart nginx # must match handler name exactly
The flow:
1
2
Config unchanged → copy task: ok → handler not triggered → no restart
Config changed → copy task: changed → handler triggered → nginx restarts
Jinja2 Templates — dynamic config files
The copy module transfers a static file — identical on every server. The template module processes Jinja2 variables first, then copies the result.
One template → infinite configurations.
Create the template
1
cp /etc/nginx/nginx.conf roles/nginx/templates/nginx.conf.j2
Replace hardcoded values with variables:
1
worker_processes ;
Use the template module
1
2
3
4
5
- name: Deploy nginx config
template:
src: nginx.conf.j2 # Ansible looks in roles/nginx/templates/
dest: /etc/nginx/nginx.conf
notify: restart nginx
Proof it works
Change nginx_worker_processes: auto to nginx_worker_processes: 2 in defaults/main.yml and run:
1
2
TASK [nginx : Deploy nginx config] ──── changed
RUNNING HANDLER [nginx : restart nginx] changed
1
2
grep worker_processes /etc/nginx/nginx.conf
# worker_processes 2;
Change it back to auto → changed=0, handler not triggered.
Final role structure
1
2
3
4
5
6
7
8
9
10
11
roles/nginx/
├── defaults/
│ └── main.yml # variables: package, service, worker_processes
├── files/
│ └── nginx.conf # static reference file
├── handlers/
│ └── main.yml # restart nginx
├── tasks/
│ └── main.yml # install → start → deploy config
└── templates/
└── nginx.conf.j2 # Jinja2 template
Key takeaways
defaults/main.ymlis the single place to change a value — it propagates everywhere- Handlers enforce idempotence for service restarts — they never run unnecessarily
- The repo is the source of truth — never edit
/etc/nginx/nginx.confdirectly on the server templateovercopyas soon as any value may differ between servers or environments
Next: Ansible Vault — encrypting secrets so passwords are never stored in plain text.