[Virtual Event] Unifying infra and app promotions with Spacelift, OpenTofu, and Kargo

Sign up ➡️

Ansible

How to Create and Use Ansible Templates [Jinja2 Examples]

How To Create and Use Templates in Ansible

Maintaining a separate configuration file for every host or environment is tedious and error-prone. Change one value, and you’re editing the same file in a dozen places. Ansible templates fix this. You write one file, mark the parts that change as variables, and Ansible generates a tailored version for each target during playbook execution.

This post covers how Ansible templating works, the Jinja2 syntax behind it, and a hands-on playbook that renders a real Nginx config from a template.

If you are new to Ansible or interested in other Ansible concepts, these Ansible tutorials on Spacelift’s blog might be handy.

What is an Ansible template?

An Ansible template is a text file written in Jinja2 that Ansible renders into a finished file, such as a configuration file, by filling in variables, loops, and conditionals with real values. The template module renders it and copies the result to the target host during playbook execution.

With Ansible templating, users can dynamically generate text-based files using templates, variables, and facts for configuration and other purposes. The main objective of using templates is to facilitate and automate the management of configuration files for different targets and requirements.

Imagine that you need to maintain multiple similar environments but with different requirements or specifications. Instead of manually creating, maintaining, and editing configuration files for each target system, we can leverage Ansible templates. We can then combine the templates with other Ansible concepts, such as facts and variables, to generate files tailored to each system’s specific needs without code duplication.

Updating configuration files becomes more manageable with this approach since we only have to perform the changes in one place and handle any inputs with variables that will be replaced with actual values during the playbook execution.

Ansible uses Jinja2 as the default templating engine to create dynamic content.

Ansible template module vs. copy module

Both modules place a file on the target host, but the template module renders it through Jinja2 first, whereas the copy module transfers it unchanged.

Use ansible.builtin.template when the file’s contents depend on variables, facts, loops, or conditionals, such as a config file that differs per host or environment. Use ansible.builtin.copy for static files that are the same everywhere, like a license file, a script, or a fixed index.html. If a file has no {{ }} or {% %} syntax, copy is the faster, simpler choice.

Templating with Jinja2

Jinja2 is a full-featured template engine for Python. The Jinja2 templating engine is quite powerful and widely used with other frameworks and applications such as Flask and Django. 

Jinja2 templates combine plain text files and special syntax to define and substitute dynamic content, embed variables, expressions, loops, and even conditional statements to generate complex output. According to the documentation, expressions are enclosed in double curly braces {{ }}, statements in curly braces with percent signs {% %}, and comments in {# #}.

Let’s have a look at some examples below:

  • Jinja2 example with a variable named favourite_color
My favourite color is {{ favourite_color }}
  • Jinja2 if statement example
{% if age > 18 %} 
You are an adult, and you can vote in the voting center: {{ voting_center }}
 {% else %}
Sorry, you are a minor, and you can’t vote yet.
{% endif %}
  • Jinja2 loop example
Here’s a list of fruits:
{% for fruit in fruits %}
{{ fruit }}
{% endfor %} 

Something worth mentioning is that the templating happens before the task is sent to the target machine. Therefore this approach doesn’t require the installation of any extra packages on the target machine and minimizes the amount of data sent. 

Another helpful functionality is utilizing the standard filters and tests included in Jinja2 to perform different operations. Ansible also implements extensions to Jinja2, including extra filters for selecting and transforming data and Lookup plugins for retrieving data from external sources.

Demo example: Create and use a template in an Ansible playbook

To use templates in Ansible playbooks, we can use the template module, which takes as inputs the template and the target file, and other necessary parameters to customize the final output file. 

In the first example, we will create a template file test.conf.j2 with the contents of the example we saw earlier.

test.conf.j2

My favourite color is {{ favourite_color }}

{% if age > 18 %}
You are an adult, and you can vote in the voting center: {{ voting_center }}
{% else %}
Sorry, you are a minor and you can’t vote yet.
{% endif %}

A list of fruits:
{% for fruit in fruits %}
 - {{ fruit }}
{% endfor %}

Now let’s create a simple playbook and use Ansible’s template module. Our playbook contains some values for the variables and only one templating task. 

test_templates_playbook.yml

- name: Playbook to test templates
 hosts: all
 vars:
   favourite_color: blue
   age: 21
   voting_center: ab456-g
   fruits:
     - banana
     - apple
     - mango
     - pear

 tasks:
    - name: Template test
      ansible.builtin.template:
        src: templates/test.conf.j2
        dest: /tmp/test.conf

If we execute this playbook the /tmp/test.conf file will be created based on the template and the inputs. Let’s check its contents after we have executed the above playbook.

/tmp/test.conf

My favourite color is blue

You are an adult, and you can vote in the voting center: ab456-g

A list of fruits:
  - banana
  - apple
  - mango
  - pear

We have used variables, if statements, and for loops to produce a final configuration file based on our input and the base template.

Finally, let’s see an example with a real use case. In the second example, we will use a template to create a configuration file for an Nginx web server.

Here’s the template that we will use.

nginx.conf.j2

server {
       listen {{ web_server_port }};
       listen [::]:{{ web_server_port }};
       root {{ nginx_custom_directory }};
       index index.html;
       location / {
               try_files $uri $uri/ =404;
       }
}

We will use this template in the playbook below to provision an Nginx web server.

main_playbook.yml

- name: Provision nginx web server
  hosts: all
  gather_facts: true
  become: true
  vars:
    nginx_custom_directory: /home/ubuntu/nginx
    web_server_port: 80
  tasks:
    - name: Update apt cache and upgrade packages
      ansible.builtin.apt:
        update_cache: true
        cache_valid_time: 3600
        upgrade: safe

    - name: Install Nginx
      ansible.builtin.apt:
        name: nginx
        state: present

    - name: Copy the Nginx configuration file to the host
      ansible.builtin.template:
        src: templates/nginx.conf.j2
        dest: /etc/nginx/sites-available/default
        mode: "0644"
        backup: true

    - name: Create link to the new config to enable it
      ansible.builtin.file:
        dest: /etc/nginx/sites-enabled/default
        src: /etc/nginx/sites-available/default
        state: link

    - name: Create Nginx directory
      ansible.builtin.file:
        path: "{{ nginx_custom_directory }}"
        state: directory
        mode: "0755"

    - name: Copy index.html to the Nginx directory
      ansible.builtin.copy:
        src: files/index.html
        dest: "{{ nginx_custom_directory }}/index.html"
        mode: "0644"

    - name: Restart the Nginx service
      ansible.builtin.service:
        name: nginx
        state: restarted

We also used a simple custom index.html file for the homepage of our web server.

index.html

<html>
  <head>
    <title> Hello from Nginx </title>
  </head>
  <body>
  <h1> This is our test webserver</h1>
  <p>This nginx web server was deployed by Ansible.</p>
  </body>
</html>

Let’s go ahead and run this playbook. For this demo, we have created a virtual machine locally with Vagrant to serve as Ansible’s target.

ansible tempates main playbook

Last step, let’s ssh into the local host, verify that everything has run successfully, and check the file /etc/nginx/sites-available/default generated from the template.

vagrant ansible templates

The templating has worked as a charm, and our web server is up and running!

How Spacelift can help you with Ansible projects

Running Ansible at scale usually means playbooks scattered across machines, no single record of what ran where, and provisioning and configuration living in two separate worlds. Spacelift sits on top of Ansible and closes that gap. 

You get one place to run playbooks, GitOps workflows triggered on pull requests, and policy checks that apply to every run.

The same control plane manages Terraform, OpenTofu, Terragrunt, Pulumi, CloudFormation, and Kubernetes, so you can combine provisioning and configuration into a single workflow instead of stitching tools together by hand.

Spacelift’s Ansible functionality solves three of the biggest challenges engineers face when using Ansible:

  • Having a centralized place in which you can run your playbooks
  • Combining infrastructure as code with configuration management to create a single workflow
  • Getting insights into what ran and where

Provisioning, configuring, governing, and even orchestrating your containers can be performed with a single workflow, separating the elements into smaller chunks to identify issues more easily.

Would you like to see this in action? Check out this video showing Spacelift’s Ansible functionality:

ansible product video thumbnail

If you want to learn more about using Spacelift with Ansible, check our documentation, read our Ansible guide, or book a demo with one of our engineers.

Key Points

This post covered Ansible’s templating capabilities. Ansible leverages Jinja2 to enable dynamic expressions and parametrization of files with variables, loops, conditions, and more. We discussed the features and syntax of Jinja2 and we saw various templating examples. Finally, we reviewed a playbook that uses the template module to produce a configuration file for a web server. 

Manage Ansible better with Spacelift

Managing large-scale playbook execution is hard. Spacelift enables you to automate Ansible playbook execution with visibility and control over resources, and seamlessly link provisioning and configuration workflows.

Learn more

Frequently asked questions

  • What is a template in Ansible?

    A template in Ansible is a Jinja2-formatted file used to create dynamic content, such as configuration files, based on variables and host-specific data. When applied using the template module, it renders the file with actual values and copies it to the target machine, enabling flexible, environment-aware automation.

  • What language is used in Ansible templates?

    Ansible templates use the Jinja2 templating language. Jinja2 allows embedding variables, conditionals, loops, and filters within files, making it ideal for generating dynamic configuration based on host data or playbook variables. The syntax is similar to Python and integrates tightly with Ansible’s variable system.

  • What is the difference between Ansible templates and files?

    Ansible templates allow dynamic content generation using Jinja2 syntax, making them suitable for configuration files that vary per host or environment. In contrast, regular files are static and copied without modification. Templates use the template module, while static files use the copy module.

Ansible Commands Cheat Sheet

Grab our ultimate cheat sheet PDF
for all the Ansible commands
and concepts you need.

Share your data and download the cheat sheet