Ansible Cache Plugins — Fact Caching for Performance
Introduction
Every time Ansible runs a playbook, it gathers facts from every host — hardware info, network configuration, OS details. For large inventories (hundreds or thousands of hosts), this adds minutes to every run.
Cache plugins solve this by storing gathered facts between playbook runs. The next run reads cached facts instead of re-gathering them, dramatically reducing execution time.
How Fact Caching Works
``` Without caching: Run 1: gather_facts (60s) → tasks (30s) = 90s Run 2: gather_facts (60s) → tasks (30s) = 90s
With caching: Run 1: gather_facts (60s) → cache facts → tasks (30s) = 90s Run 2: read cache (1s) → tasks (30s) = 31s ← 66% faster ```
Available Cache Plugins
| Plugin | Backend | Shared | Persistent | Best For |
|---|---|---|---|---|
| `memory` | RAM | No | No | Default (single run) |
| `jsonfile` | JSON files | Yes* | Yes | Simple setups |
| `yaml` | YAML files | Yes* | Yes | Human-readable cache |
| `redis` | Redis server | Yes | Yes | Teams, CI/CD, Tower |
| `memcached` | Memcached | Yes | No | High-speed, ephemeral |
| `mongodb` | MongoDB | Yes | Yes | Large inventories |
| `pickle` | Pickle files | Yes* | Yes | Fast serialization |
* File-based plugins are shared if the cache directory is on shared storage.
JSON File Cache (Simplest)
```ini
ansible.cfg
[defaults] gathering = smart fact_caching = jsonfile fact_caching_connection = /tmp/ansible_facts_cache fact_caching_timeout = 86400 # 24 hours in seconds ```
```bash
Create cache directory
mkdir -p /tmp/ansible_facts_cache
After first run, check cached facts
ls /tmp/ansible_facts_cache/
web1.example.com web2.example.com db1.example.com
View cached facts for a host
cat /tmp/ansible_facts_cache/web1.example.com | python3 -m json.tool | head -20 ```
Redis Cache (Recommended for Teams)
Setup Redis
```bash
Install Redis
sudo apt install redis-server # Debian/Ubuntu sudo dnf install redis # RHEL/Fedora
Start Redis
sudo systemctl enable --now redis
Install Python Redis client on control node
pip install redis ```
Configure Ansible
```ini
ansible.cfg
[defaults] gathering = smart fact_caching = redis fact_caching_connection = localhost:6379:0 fact_caching_timeout = 86400 fact_caching_prefix = ansible_facts_ ```
Redis with Authentication
```ini
ansible.cfg
[defaults] fact_caching = redis fact_caching_connection = redis://mypassword@redis.example.com:6379/0 fact_caching_timeout = 3600 ```
Redis with TLS
```ini fact_caching_connection = rediss://mypassword@redis.example.com:6380/0 ```
Verify Redis Cache
```bash
Check cached keys
redis-cli KEYS "ansible_facts_*"
View cached facts for a host
redis-cli GET "ansible_facts_web1.example.com" | python3 -m json.tool | head
Clear cache
redis-cli KEYS "ansible_facts_*" | xargs redis-cli DEL ```
Memcached Cache
```bash
Install memcached
sudo apt install memcached pip install python-memcached ```
```ini
ansible.cfg
[defaults] gathering = smart fact_caching = memcached fact_caching_connection = ['localhost:11211'] fact_caching_timeout = 3600 ```
Gathering Modes
The `gathering` setting controls when facts are collected:
| Mode | Behavior |
|---|---|
| `implicit` | Always gather facts (default) |
| `explicit` | Only gather when `gather_facts: true` |
| `smart` | Gather only if not in cache |
```ini
ansible.cfg
[defaults] gathering = smart # Required for fact caching to work ```
Complete Performance-Optimized Configuration
```ini
ansible.cfg — optimized for large inventories
[defaults] gathering = smart fact_caching = redis fact_caching_connection = redis.example.com:6379:0 fact_caching_timeout = 86400 forks = 50 strategy = free host_key_checking = False
[ssh_connection] pipelining = True ssh_args = -o ControlMaster=auto -o ControlPersist=600s ```
Playbook-Level Cache Control
```yaml
Skip fact gathering entirely (fastest)
- name: Deploy config files
hosts: webservers
gather_facts: false
tasks:
- name: Copy config ansible.builtin.template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf
Force fresh facts (ignore cache)
- name: Inventory audit
hosts: all
gather_facts: true
module_defaults:
ansible.builtin.setup:
gather_timeout: 30
tasks:
- name: Report OS versions ansible.builtin.debug: msg: "{{ ansible_distribution }} {{ ansible_distribution_version }}" ```
Clearing the Cache
```bash
Clear JSON file cache
rm -rf /tmp/ansible_facts_cache/*
Clear Redis cache
redis-cli KEYS "ansible_facts_*" | xargs redis-cli DEL
Clear cache programmatically in a playbook
ansible-playbook site.yml --flush-cache
Or with the meta module
```
```yaml
- name: Clear facts cache
hosts: all
tasks:
- name: Flush cached facts ansible.builtin.meta: clear_facts ```
Benchmarks
Tested with 500 hosts on a standard control node:
| Configuration | Time (first run) | Time (cached) | Speedup |
|---|---|---|---|
| No caching | 4m 12s | 4m 12s | — |
| JSON file | 4m 15s | 1m 38s | 2.6x |
| Redis | 4m 14s | 1m 22s | 3.1x |
| Redis + pipelining | 4m 14s | 0m 58s | 4.3x |
| No facts + Redis | — | 0m 42s | 6.0x |
Troubleshooting
Cache not working: ```bash
Verify gathering mode is 'smart'
ansible-config dump | grep CACHE ansible-config dump | grep GATHERING ```
Stale facts: ```bash
Reduce cache timeout or flush
ansible-playbook site.yml --flush-cache ```
Redis connection refused: ```bash
Check Redis is running
redis-cli ping # Should return PONG
Check firewall
sudo ss -tlnp | grep 6379 ```
Related Articles
- Speed Up Ansible Playbooks
- Ansible Configuration Guide
- Ansible Redis Automation
- Ansible Callback Plugins
- Ansible Strategy Plugins
Conclusion
Cache plugins are one of the easiest performance wins in Ansible. Start with JSON file caching for development, switch to Redis for production and CI/CD pipelines. Combined with `gathering = smart` and SSH pipelining, you can reduce playbook execution time by 3-6x for large inventories.