The ansible.builtin.template Module
The template module processes Jinja2 template files (.j2) and deploys the rendered output to remote hosts. Variables, loops, and conditionals make your configs dynamic.
Basic Usage
Template file (templates/nginx.conf.j2):
server {
listen {{ http_port }};
server_name {{ server_name }};
location / {
proxy_pass http://127.0.0.1:{{ app_port }};
}
}
Playbook:
- name: Deploy nginx config
ansible.builtin.template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/myapp.conf
owner: root
group: root
mode: '0644'
notify: Restart nginx
vars:
http_port: 80
server_name: example.com
app_port: 8080
Variables in Templates
From Playbook vars
vars:
db_host: db.example.com
db_port: 5432
db_name: myapp
{# templates/database.conf.j2 #}
[database]
host = {{ db_host }}
port = {{ db_port }}
name = {{ db_name }}
From Ansible Facts
{# templates/motd.j2 #}
Welcome to {{ ansible_facts['hostname'] }}
OS: {{ ansible_facts['distribution'] }} {{ ansible_facts['distribution_version'] }}
IP: {{ ansible_facts['default_ipv4']['address'] }}
CPUs: {{ ansible_facts['processor_vcpus'] }}
RAM: {{ ansible_facts['memtotal_mb'] }} MB
Conditionals
{# templates/app.conf.j2 #}
[app]
debug = {{ 'true' if env == 'development' else 'false' }}
log_level = {{ 'DEBUG' if env == 'development' else 'INFO' }}
{% if enable_ssl %}
[ssl]
cert_file = /etc/ssl/certs/app.crt
key_file = /etc/ssl/private/app.key
{% endif %}
{% if ansible_facts['os_family'] == 'Debian' %}
log_path = /var/log/myapp/
{% elif ansible_facts['os_family'] == 'RedHat' %}
log_path = /var/log/myapp/
{% endif %}
Loops
{# templates/hosts.j2 #}
# Generated by Ansible
{% for host in groups['webservers'] %}
{{ hostvars[host]['ansible_host'] }} {{ host }}
{% endfor %}
{# templates/upstream.conf.j2 #}
upstream app_servers {
{% for host in groups['appservers'] %}
server {{ hostvars[host]['ansible_host'] }}:{{ app_port }} weight=1;
{% endfor %}
}
Loop with Index
{% for user in users %}
# User {{ loop.index }}: {{ user.name }}
{{ user.name }}:x:{{ 1000 + loop.index0 }}:{{ 1000 + loop.index0 }}::/home/{{ user.name }}:/bin/bash
{% endfor %}
Filters
{# Common Jinja2 filters #}
{{ variable | default('fallback_value') }}
{{ variable | upper }}
{{ variable | lower }}
{{ list_var | join(', ') }}
{{ password | password_hash('sha512') }}
{{ dict_var | to_nice_json }}
{{ dict_var | to_nice_yaml }}
{{ path | basename }}
{{ path | dirname }}
{{ string | regex_replace('old', 'new') }}
{{ number | int }}
{{ ip_list | ipaddr }}
Practical Examples
systemd Service File
{# templates/myapp.service.j2 #}
[Unit]
Description={{ app_name }} Service
After=network.target
{% if db_host is defined %}
After=postgresql.service
{% endif %}
[Service]
Type=simple
User={{ app_user }}
Group={{ app_group }}
WorkingDirectory={{ app_dir }}
ExecStart={{ app_dir }}/bin/{{ app_name }} --port {{ app_port }}
Restart=always
RestartSec=5
Environment=NODE_ENV={{ env }}
{% for key, value in env_vars.items() %}
Environment={{ key }}={{ value }}
{% endfor %}
[Install]
WantedBy=multi-user.target
Environment File
{# templates/app.env.j2 #}
# Generated by Ansible — do not edit manually
APP_NAME={{ app_name }}
APP_PORT={{ app_port }}
DATABASE_URL=postgresql://{{ db_user }}:{{ db_password }}@{{ db_host }}:{{ db_port }}/{{ db_name }}
REDIS_URL=redis://{{ redis_host }}:{{ redis_port }}
SECRET_KEY={{ secret_key }}
LOG_LEVEL={{ log_level | default('INFO') }}
SSH Config
{# templates/sshd_config.j2 #}
Port {{ ssh_port | default(22) }}
PermitRootLogin {{ 'yes' if allow_root_login else 'no' }}
PasswordAuthentication {{ 'yes' if allow_password_auth else 'no' }}
MaxAuthTries {{ max_auth_tries | default(3) }}
{% for user in ssh_allowed_users %}
AllowUsers {{ user }}
{% endfor %}
Template with Validation
- name: Deploy nginx config with validation
ansible.builtin.template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
validate: nginx -t -c %s
notify: Restart nginx
- name: Deploy sudoers with validation
ansible.builtin.template:
src: templates/sudoers.j2
dest: /etc/sudoers
validate: visudo -cf %s
mode: '0440'
Parameters
| Parameter | Description | Example |
|---|---|---|
src | Template file path | templates/app.conf.j2 |
dest | Remote destination | /etc/app/config |
mode | File permissions | '0644' |
owner | File owner | root |
group | File group | root |
validate | Validation command | nginx -t -c %s |
backup | Create backup | true |
force | Overwrite always | true |
Browse 800+ Ansible tutorials on AnsibleByExample.