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

ParameterDescriptionExample
srcTemplate file pathtemplates/app.conf.j2
destRemote destination/etc/app/config
modeFile permissions'0644'
ownerFile ownerroot
groupFile grouproot
validateValidation commandnginx -t -c %s
backupCreate backuptrue
forceOverwrite alwaystrue

Browse 800+ Ansible tutorials on AnsibleByExample.