Ansible Template Module

The template module is one of the most-used modules in all of Ansible. It combines Jinja2 rendering with file deployment, validation, backup, and permission management in a single idempotent operation. This lesson covers everything you need to use the template module effectively in production — including the patterns that prevent you from ever deploying a broken config file again.

Template vs Copy: When to Use Each

The copy module transfers a file verbatim from the control node to managed nodes. The template module renders a Jinja2 template first and then deploys the result. Use copy for static files that are identical on all hosts (SSL CA bundles, static scripts, binary files). Use template for any file whose content should vary by host, group, or variable value — which is most config files in practice.

Basic Template Module Usage

- name: Deploy Nginx virtual host configuration
  template:
    src: nginx-vhost.conf.j2    # Path relative to templates/ directory (in a role)
    dest: /etc/nginx/sites-available/myapp.conf
    owner: root
    group: root
    mode: '0644'
  notify: Reload Nginx

When used inside a role, the src path is automatically resolved relative to roles/rolename/templates/. When used in a standalone playbook, the path is relative to the playbook file's location.

The validate Parameter: Never Deploy a Broken Config

The validate parameter runs a command against the rendered template before deploying it. If the command fails (non-zero exit code), Ansible aborts the task and does not replace the existing file. This is the single most important production safety mechanism available in the template module:

- name: Deploy Nginx configuration
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    validate: nginx -t -c %s   # %s is replaced with a temp file path

- name: Deploy sudoers configuration
  template:
    src: sudoers.j2
    dest: /etc/sudoers
    validate: visudo -cf %s    # Validates sudoers syntax before replacing

- name: Deploy sshd_config
  template:
    src: sshd_config.j2
    dest: /etc/ssh/sshd_config
    validate: sshd -t -f %s    # Validate SSH server config syntax
  notify: Restart SSH

Make it a habit to use validate whenever the target application provides a syntax check command. For configs without a dedicated checker, consider using a custom script that parses the file.

Backup Parameter: Safe Config Management

- name: Deploy application configuration with backup
  template:
    src: app.conf.j2
    dest: /etc/myapp/config.yml
    backup: true
    owner: root
    mode: '0640'

The backup: true parameter creates a timestamped backup of the existing file before replacing it. Backup files are created in the same directory as the target, named with a timestamp suffix like config.yml.2024-03-15@14:32:05~. This is a lightweight rollback mechanism — if the new config causes problems, the backup is immediately available.

Controlling File Permissions

- name: Deploy secrets file with strict permissions
  template:
    src: database.conf.j2
    dest: /etc/myapp/database.conf
    owner: myapp
    group: myapp
    mode: '0600'    # Owner read/write only — no group or other access

Always set explicit permissions on config files. The mode field uses octal notation: '0644' for world-readable configs, '0640' for configs readable by the owner and group, '0600' for secrets that should only be readable by the owning process.

force Parameter: Controlling Overwrites

- name: Deploy initial config only if file does not exist
  template:
    src: initial-config.j2
    dest: /etc/myapp/config.yml
    force: false   # Never overwrite an existing file

With force: false, the template is deployed only if the destination file does not exist. This is useful for initial setup tasks where you want Ansible to create a default config but not overwrite manual customisations made by operators after the initial deployment.

Deploying Templates from Variables

- name: Deploy environment-specific configuration
  template:
    src: "{{ app_env }}-config.j2"
    dest: /etc/myapp/config.yml
  vars:
    app_env: production

The src parameter accepts Jinja2 expressions, enabling dynamic template selection. This pattern deploys different templates based on the environment, application version, or any other variable — a clean alternative to conditionals inside a single large template.

Looping Over Template Deployments

- name: Deploy multiple virtual host configs
  template:
    src: vhost.conf.j2
    dest: "/etc/nginx/sites-available/{{ item.server_name }}.conf"
    owner: root
    mode: '0644'
  loop: "{{ nginx_vhosts }}"
  notify: Reload Nginx

Each loop iteration renders the template with the current item variable available alongside all other variables. The item.server_name in the dest path creates a unique filename for each virtual host.

Checking Rendered Template Content Before Deploying

Use the --check and --diff flags together to see exactly what would change before applying any modifications:

ansible-playbook site.yml --check --diff

The --diff output shows a unified diff of the current file content vs the rendered template output. This is invaluable for reviewing config changes before applying them in production — treat it as a mandatory step before any config deployment in a live environment.

Real-World Template Deployment Pattern

A complete, production-hardened template task:

- name: Deploy and validate Nginx main configuration
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: '0644'
    backup: true
    validate: nginx -t -c %s
  notify:
    - Test Nginx configuration
    - Reload Nginx

Try This: Safe Config Deployment Workflow

Write a playbook task that deploys an Nginx config with validation, backup, and correct permissions. Before running it normally, run with --check --diff to preview the changes. Then deliberately introduce a syntax error in your .j2 template and run the playbook again — confirm that the validate parameter stops the deployment and the existing config is left intact. This exercise builds confidence in the safety of the template module's built-in protections.

Summary

The template module renders Jinja2 templates and deploys them with full control over permissions, ownership, and backup. The validate parameter is the most important production safety feature — it prevents broken configs from being deployed by running a syntax check before replacing the existing file. The backup parameter creates timestamped backups for easy rollback. Using --check --diff before production deployments provides a clear preview of all changes. The template module is the correct tool for any config file that varies between hosts or environments.

Leave a Comment