Ansible regex_replace Filter: Find & Replace with Regex (Complete Guide)
By Luca Berton · Published 2024-01-01 · Category: installation
How to find and replace text with regex in Ansible using regex_replace filter. Pattern matching, capture groups, string substitution.
The regex_replace filter in Ansible lets you perform regex-based find-and-replace operations on strings directly in your playbooks. It's essential for transforming hostnames, cleaning data, rewriting configuration values, and manipulating text without external tools.
Basic Syntax
{{ string | regex_replace(pattern, replacement) }}
• pattern — Python regular expression to match
• replacement — Replacement string (supports backreferences \\1, \\2)
- name: Simple regex replace
ansible.builtin.debug:
msg: "{{ 'Hello World' | regex_replace('World', 'Ansible') }}"
# Output: "Hello Ansible"
See also: Ansible default() Filter: Set Fallback Values for Undefined Variables
Common Use Cases
Remove Characters from a String
- name: Remove all non-alphanumeric characters
ansible.builtin.debug:
msg: "{{ 'web-server_01.example.com' | regex_replace('[^a-zA-Z0-9]', '') }}"
# Output: "webserver01examplecom"
- name: Remove trailing whitespace
ansible.builtin.debug:
msg: "{{ my_string | regex_replace('\\s+$', '') }}"
- name: Remove comments from config line
ansible.builtin.debug:
msg: "{{ 'max_connections = 200 # default is 100' | regex_replace('\\s*#.*$', '') }}"
# Output: "max_connections = 200"
Extract and Transform with Capture Groups
- name: Swap first and last name
ansible.builtin.debug:
msg: "{{ 'Berton, Luca' | regex_replace('^(\\w+),\\s*(\\w+)$', '\\2 \\1') }}"
# Output: "Luca Berton"
- name: Extract version number
ansible.builtin.debug:
msg: "{{ 'ansible-core-2.16.0.tar.gz' | regex_replace('^.*-(\\d+\\.\\d+\\.\\d+).*$', '\\1') }}"
# Output: "2.16.0"
- name: Reformat date (MM/DD/YYYY → YYYY-MM-DD)
ansible.builtin.debug:
msg: "{{ '04/12/2026' | regex_replace('(\\d{2})/(\\d{2})/(\\d{4})', '\\3-\\1-\\2') }}"
# Output: "2026-04-12"
Add Prefix or Suffix
- name: Add prefix to each word
ansible.builtin.debug:
msg: "{{ 'nginx redis postgres' | regex_replace('(\\w+)', 'pkg_\\1') }}"
# Output: "pkg_nginx pkg_redis pkg_postgres"
- name: Wrap string in quotes
ansible.builtin.debug:
msg: "{{ my_value | regex_replace('^(.*)$', '\"\\1\"') }}"
Clean Hostnames and URLs
vars:
fqdn: "web-server-01.prod.example.com"
tasks:
- name: Extract short hostname
ansible.builtin.debug:
msg: "{{ fqdn | regex_replace('\\..*$', '') }}"
# Output: "web-server-01"
- name: Extract domain
ansible.builtin.debug:
msg: "{{ fqdn | regex_replace('^[^.]+\\.', '') }}"
# Output: "prod.example.com"
- name: Convert hostname to underscore format
ansible.builtin.debug:
msg: "{{ fqdn | regex_replace('[.-]', '_') }}"
# Output: "web_server_01_prod_example_com"
Case-Insensitive Matching
Use the (?i) flag at the start of the pattern:
- name: Replace case-insensitively
ansible.builtin.debug:
msg: "{{ 'Hello WORLD hello World' | regex_replace('(?i)hello', 'hi') }}"
# Output: "hi WORLD hi World"
See also: Ansible map Filter: Extract Attributes from Lists (Complete Guide)
Multiline Patterns
Use (?m) for multiline mode (where ^ and $ match line boundaries) and (?s) for dotall mode (where . matches newlines):
- name: Remove comment lines from multiline string
ansible.builtin.debug:
msg: "{{ my_config | regex_replace('(?m)^#.*\\n?', '') }}"
- name: Replace content between markers
ansible.builtin.debug:
msg: "{{ content | regex_replace('(?s)BEGIN.*?END', 'REPLACED') }}"
Use with map for List Transformation
vars:
servers: ['web-01', 'web-02', 'db-01']
tasks:
- name: Convert hyphens to underscores in all items
ansible.builtin.debug:
msg: "{{ servers | map('regex_replace', '-', '_') | list }}"
# Output: ['web_01', 'web_02', 'db_01']
- name: Add domain to all hostnames
ansible.builtin.debug:
msg: "{{ servers | map('regex_replace', '$', '.example.com') | list }}"
# Output: ['web-01.example.com', 'web-02.example.com', 'db-01.example.com']
See also: Ansible Jinja2 Filters: Transform Data in Playbooks (Complete Reference)
Use in Templates (Jinja2)
{% for host in groups['all'] %}
{{ host | regex_replace('\\..*$', '') }} ansible_host={{ hostvars[host]['ansible_host'] }}
{% endfor %}
Use in Conditionals
- name: Only run for production servers
ansible.builtin.debug:
msg: "Production server detected"
when: inventory_hostname | regex_replace('^.*-', '') == 'prod'
regex_replace vs replace
| Feature | replace | regex_replace |
|---------|-----------|-----------------|
| Pattern type | Literal string | Regular expression |
| Capture groups | ❌ | ✅ (\\1, \\2) |
| Flags (case, multiline) | ❌ | ✅ ((?i), (?m)) |
| Performance | Faster | Slightly slower |
| Use when | Simple literal replacement | Pattern matching needed |
# Simple literal — use replace
{{ 'hello-world' | replace('-', '_') }}
# Pattern matching — use regex_replace
{{ 'hello-world-123' | regex_replace('-\\d+$', '') }}
regex_replace vs regex_search
•regex_replace — Replaces matched text with something else (transformation)
• regex_search — Extracts the first match from a string (extraction)
# regex_replace: transform the string
{{ 'version: 2.16.0' | regex_replace('^version:\\s*', '') }}
# Output: "2.16.0"
# regex_search: extract a match
{{ 'version: 2.16.0' | regex_search('\\d+\\.\\d+\\.\\d+') }}
# Output: "2.16.0"
Real-World Playbook Examples
Sanitize User Input
- name: Clean user-provided hostname
ansible.builtin.set_fact:
clean_hostname: "{{ raw_hostname | regex_replace('[^a-zA-Z0-9.-]', '') | regex_replace('^-+|-+$', '') | lower }}"
Generate Config from Inventory
- name: Build upstream block for nginx
ansible.builtin.copy:
content: |
upstream backend {
{% for host in groups['app_servers'] %}
server {{ hostvars[host]['ansible_default_ipv4']['address'] }}:{{ app_port | default(8080) }}; # {{ host | regex_replace('\\..*$', '') }}
{% endfor %}
}
dest: /etc/nginx/conf.d/upstream.conf
Normalize Package Names
- name: Remove version suffixes from package list
ansible.builtin.set_fact:
package_names: "{{ installed_packages | map('regex_replace', '-\\d+[.\\d]*.*$', '') | unique | list }}"
Convert Between Naming Conventions
- name: CamelCase to snake_case
ansible.builtin.debug:
msg: "{{ 'MyVariableName' | regex_replace('([A-Z])', '_\\1') | regex_replace('^_', '') | lower }}"
# Output: "my_variable_name"
- name: snake_case to kebab-case
ansible.builtin.debug:
msg: "{{ 'my_variable_name' | regex_replace('_', '-') }}"
# Output: "my-variable-name"
Escaping Special Characters
When your pattern contains special regex characters, escape them with \\:
# Match literal dots
{{ hostname | regex_replace('\\.', '_') }}
# Match literal brackets
{{ text | regex_replace('\\[(.+?)\\]', '(\\1)') }}
# Match literal dollar sign
{{ price | regex_replace('\\$', 'USD ') }}
FAQ
How do I replace text with regex in Ansible?
Use the regex_replace filter: {{ my_string | regex_replace('pattern', 'replacement') }}. The pattern is a Python regular expression. Use \\1, \\2 for capture group backreferences in the replacement string.
What is the difference between replace and regex_replace in Ansible?
The replace filter matches literal strings only. The regex_replace filter matches regular expression patterns, supports capture groups (\\1), and allows flags like (?i) for case-insensitive matching. Use replace for simple text; use regex_replace when you need pattern matching.
How do I use capture groups in regex_replace?
Wrap parts of your pattern in parentheses to create capture groups, then reference them as \\1, \\2 in the replacement: {{ 'John Doe' | regex_replace('^(\\w+) (\\w+)$', '\\2, \\1') }} outputs Doe, John.
How do I do case-insensitive regex replace in Ansible?
Add (?i) at the start of your pattern: {{ text | regex_replace('(?i)error', 'warning') }}. This makes the entire pattern case-insensitive.
Can I use regex_replace in a loop with map?
Yes, use map to apply regex_replace to every item in a list: {{ my_list | map('regex_replace', 'old', 'new') | list }}.
Conclusion
The regex_replace filter is essential for string transformation in Ansible:
• Simple replacements: string | regex_replace('pattern', 'new')
• Capture groups: Use () in pattern, \\1 in replacement
• Case-insensitive: Add (?i) prefix to pattern
• List transformation: Chain with map filter
• Choose wisely: Use replace for literals, regex_replace for patterns
Related Articles
• Ansible regex_search Filter: Pattern Matching & Text Extraction Guide • Filter a List by Its Attributes: Ansible selectattr Filter • Edit Single Line Text: Ansible Module lineinfile • Ansible map Filter: Extract Attributes from Lists • Ansible split Filter Complete GuideCategory: installation